React dependency array is not to be ignored

Magda Odrowąż-Żelezik
Fink IT
Published in
6 min readJul 8, 2022

Following dependency array rules lead to better design of code

Photo by Francesco Ungaro: https://www.pexels.com/photo/brown-steel-frame-during-day-time-96081/

We all know this pattern in creating functional components when you want to have your functions run only on component mount:

useEffect(() => {
functionToRunOnlyOnMount()
}, [])

It is prescribed by many and better yet, working.

Problem start, when you run linting on your code and see this warning:

warning  React Hook useEffect has a missing dependency: 'functionToRunOnlyOnMount'. Either include it or remove the dependency array  react-hooks/exhaustive-deps

After googling this issue you may find a lot of magic recipes on how to suppress or switch off this eslint warning. All of them are obviously wrong, because you should take this warning seriously — but to do so, it is important to understand why. Let’s do that from the scratch.

What is a dependency array

To explain what a dependency array is, let’s start with the example of a popular hook called useEffect. useEffect is designed for managing and controlling side effects of a functional component. A side effect is a modification of state or a value outside the scope of the function.

const [ count, setCount ] = useState(0)useEffect(() => {
setCount(count + 1)
}, [])

In this example useEffect is taking a variable from the outer scope and is modifying its value using useState hook. This means, that if count was initially modified somewhere else, it’s value is already different at the moment. Therefore we cannot expect the output for given input, as the input is out of scope.

By React rules, performing functions that are not pure functions (aka do not have any side effects) is not permitted except by the use of a hook. Here is where useEffectcomes in handy. Another added value is that this hook is triggered after the render phase is completed, which means it can operate on the new, updated state of the component. This is why it is so heavily used to run functions after component mount, such as data fetching functions (for example GET requests).

What is then a dependency array? It is a collection of values the hook first argument function is dependent on. In other words, if any of the items in the dependency array changes its value, the first argument function should be called using this new value.

Dependency array is optional. If there is none, the first argument function will rerun at every re-render of the component. If you provide an empty dependency array, the function will run only on the first render of the component. Therefore useEffectis most commonly used to run functions conditionally or on the component mount.

It’s not that simple

I saw many cases over the network where people use the above functionality for their own purpose. It is not designed for this though. It is deliberately designed for a developer to include all the dependencies that the hook relies on — no exceptions. Ignoring this can lead to bugs that are very difficult to find and deal with. Moreover, this is going against the React rules of conduct, which is always a bad idea.

Does that make you say: but my function is using a lot of variables and other functions, if I had to add everything to the array it would be crazy long?

Well, you might want to rethink your logic as a whole.

You should care about dependency array

Why? Let’s recap what was written above in simpler terms:

React development team wants you to

They wrote the damn library. Please obey.

Ignoring dependency array rules can lead to bugs

useEffect hook runs after the render phase is finished. The values have already changed at this point. You might find yourself trying to access variable that already has different value and not knowing the reason why.

And ultimately:

It leads to better design

Please refer to the paragraph explaining what dependency array is. It points out importance of using pure functions and maintaining side effects. It is important to understand this thoroughly, as those are not just useless paradigms.

Try to imagine a scenario: you have a view of a table. Table should be polluted with the data from an API. It is pretty long, so the API uses some sort of pagination. There are some conditional situations to handle, like: what do to when there is no more items, what to do when there is 0 items etc. You wrote a nice component dealing with all this logic. It is pretty complex, but with nifty use of states you managed to deal with all this in less than 100 lines of code within your useEffect hook, that handles the mounting state.

Considering that you chose to not ignore the dependency array warning now, you add all the suggested variables and functions. Suddenly your array is five, six, ten arguments long! This doesn’t look nice, does it?

Long list of dependency array arguments should trigger the instant thought in your mind: this useEffect will call the first argument function (aka your whole complex logic) every time any of these values changes! This is not what you wanted, you wanted it to run on the component mount.

You can do one of three things now:
1. Panic and try to suppress the rerun of the function with variety of useCallback or flags
2. Remove items from the dependency array, add eslint warning suppression and go on with your day
3. Think about what you are doing for a second.

Pure intentions = pure functions

In the presented imaginary scenario we are dealing with a pretty complex logic. This is a lot for a function to handle, and this is definitely a lot for a brain to handle, trying to assess on the first look — what the actual intentions of the developer were? What do they want to do here? Fetch the data and update the state and check the count and fetch more data and this and that… If you need to use and in your assessment, there is a good change you are not dealing with a pure function and complicating your life.

One: separate

Ideally your function should do one thing. For an input it should do its magic and produce an output. That’s it. Try to separate your logic into separate sections.

With this approach, you are making things much easier for the future you. With one look at a function you will be able to tell what it is doing.

Two: delegate

Once you have separated your logic, you can make them delegate their outputs to each other. This way again you make sure that your functions do their own thing, and something else handles it further.

Three: extract

Now you can move your logic around. If you did your work correctly, perhaps some of the functions don’t even need to reside in the React component anymore.

Eventually you will be left with completely indispensable logic that must run on component mount. The output of the function can be saved to another state, that is being handled by yet another useEffect . This way, you dependency array got cleared to completely necessary values it must have to proceed with your operation! Still not empty? You might want to return to step one.

Benefits

  1. You end up with the readable, clear code. Future you or other developers like you very much for making them not waste time on trying to figure out what is going on.
  2. Your functions are transparent and independent, making them possibly reusable.
  3. You avoid potential bugs and unnecessary loops caused by incorrect triggers of render
  4. You follow the React rules of conduct, possibly making your app better optimized for this potent library
  5. You learn healthy practices that eventually drive you further from looking into naive answers on forums and closer to delivering valuable software really fast

Conclusion + tip

Dependency arrays matter and should not be ignored or suppressed. This approach is naive and can backfire. Most importantly though, even if nothing bad would happen, you can use dependency arrays complicity to your own benefit and learn how to write better, more efficient and legible code.

And tip for the end: all potential recipes for troubles with dependency arrays you can find in React docs! Don’t underestimate the docs.

--

--

Magda Odrowąż-Żelezik
Fink IT
Editor for

Creative front end developer, currently excited about learning 3D graphics. Visit magdazelezik.com