TypeOfNaN

You probably shouldn't ignore react-hooks/exhaustive-deps linting warnings

Nick Scialli
August 03, 2021

It’s tempting—we’ve all been there. We get the react-hooks/exhaustive-deps linting warning about a dependency array in a React hook. We try to add the dependency to the array and then we end up with infinite re-renders or otherwise undesirable behavior.

We have a seemingly-easy out: we can ignore the linting error by placing a // eslint-disable-next-line right above the dependency array. Here’s a contrived example:

useEffect(() => {
  setCount(count + 1);
  // eslint-disable-next-line
}, []);

And this indeed works. But I have found out over time that it’s worth the effort to not disable these warnings and, instead, find a way to fulfill the dependency array requirements.

But why?

There are a couple reasons this is worth the effort:

  1. Your probably actually do want to fire the effect (or whatever hook you’re dealing with) when the variable changes. It might not seem like it at first, but there are probably some edge cases for which your effect logic will fail if you don’t include the variable in the dependency array.
  2. Once you add the // eslint-disable-next-line, you have now given up the opportunity to be warned about future dependencies. If you add a new dependency to the effect, you won’t be warned about forgetting to include it in the dependency array. This could be a big deal and break your app.

How to go about fixing the issue

The more appropriate fix for this warning depends on the situation.

Stateful dependencies

In the aforementioned contrived example, you could likely get away with using a callback in your setCount method:

useEffect(() => {
  setCount((count) => count + 1);
}, []);

Now, count is not a dependency, so our problem is solved.

Function dependency

Let’s consider the following example in which a function is declared in the body of the component.

const someFunction = (count) => {
  // do something with count
};

useEffect(() => {
  someFunction(count);
}, [count]);

We get a dependency warning again because someFunction should be in the dependency array. If we add it directly to the dependency array, we potentially get warned that we have just caused an infinite loop because someFunction has a different reference on each render.

The fix here is likely to use a useCallback hook to ensure someFunction doesn’t change references between renders (unless it itself needs to have dependencies in its dependency array!).

const someFunction = useCallback((count) => {
  // do something with count
}, []);

useEffect(() => {
  someFunction(count);
}, [count, someFunction]);

When all else fails

When all else fails it’s quite possible you can just do some basic comparison within the effect and bailing out as necessary. In the following example we return early from the effect if the post id is undefined.

useEffect(() => {
  if (post.id === undefined) {
    return;
  }
  doSomethingWithPost(post);
}, [post]);

Conclusion

I hope this post has convinced you it’s worth trying to fulfill react hook dependency requirements and has given you some ways to do so.

If you'd like to support this blog by buying me a coffee I'd really appreciate it!

Nick Scialli

Nick Scialli is a senior UI engineer at Microsoft.

© 2024 Nick Scialli