"Function makes the dependencies of useEffect Hook change on every render" warning in React
Tags: javascript, programming, react
Published: 2023-03-07 19:17:39

If you work with the useEffect hook, you have likely encountered the following warning:

> The ‘functionName’ function makes the dependencies of useEffect Hook (at line X) change on every render. Move it inside the useEffect callback. Alternatively, wrap the definition of ‘functionName’ in its own useCallback() Hook. (react-hooks/exhaustive-deps)

So what’s going on here and how do we fix it?

## A contrived example
Let’s check a quick example. We’ll have a simple react app and updateCount function which we will call inside useEffect hooks and we need to pass the function name in dependency array or it will show `React Hook useEffect has missing dependency warning`. What we want here is every time count changes, we want to run an effect that logs the count to the console. So we write the following code:

```
function App() {
  const [count, setCount] = useState(0);

  const updateCount = () => {
    console.log(count);
  };

  useEffect(() => {
    updateCount();
  }, [updateCount]);

  return <div>{count}</div>;
}
```
And now we see the warning! So, lets use it to explore what’s happening.

## Dependency arrays and referential equality
In our example, our useEffect hook has the updateCount function in its dependency array. That means the effect will run every time after updateCount changes.

You might be thinking that updateCount is a function and it doesn’t really changes after each render. But we have to remember that JavaScript equality works based on referential equality. According to the principle of Object.is(), any two objects pointing to the exact memory location are considered equal or only equal to each other if they reference the same object in memory. That’s why when we compare two objects with same key and value we end up with something like this:

```
console.log({ name: 'foo' } === { name: 'foo' });
// false
```
These two objects live separately in memory and are therefore not equal.

This works the same way for functions:

```
const fn1 = () => 'bar';
const fn2 = () => 'bar';

console.log(fn1 === fn2);
// false
```
And this is what’s happening in our useEffect dependency array. The updateCount function is recreated on each render and so it be will always be different than the updateCount function created during the previous render.

## How to fix this?
Well, the warning message is actually pretty helpful and itself gives us the solutions that will help us fix the problem:

- Move updateCount inside the useEffect hook
- Wrap updateCount in a useCallback hook

I’ll show you both solution here.

**Moving updateCount inside the useEffect hook**
This one is fairly straightforward: we take the updateCount function and move it inside the useEffect hook. Now the  updateCount is now inside the useEffect hook, we no longer need it in the dependency array. However, since updateCount depends on the count variable, we’ll have to add the count variable to the dependency array.

Our fixed code now looks like this:

```
function App() {
  const [count, setCount] = useState(1);

  useEffect(() => {
    const updateCount = () => {
      console.log(count);
    };
    updateCount();
  }, [count]);

  return <div className="App">{count}</div>;
}
```
And the warning is gone!

**Wrapping updateCount in a useCallback hook**
Let's consider a situation in which you need updateCount to call elsewhere in the component, putting it inside the effect could cause it to be undefined elsewhere.

For this solution we need to wrap the updateCount function definition in a useCallback hook. What this does is returns a memoized function whose reference will only change if something in the hook’s dependency array changes.

Let’s take a look at the correct implementation:

```
import { useState, useEffect, useCallback } from 'react';

function App() {
  const [count, setCount] = useState(1);

  const updateCount = useCallback(() => {
    console.log(count);
  }, [count]);

  useEffect(() => {
    updateCount();
  }, [updateCount]);

  return <div className="App">{count}</div>;
}
```
Now that we have wrapped our updateCount function in a useCallback hook, it will maintain the same memory reference through each render—unless something in its dependency array changes. In this case, we added count to the dependency array. We need the function to update when count updates or our effect won’t run.

## Conclusion
React dependency array issues can be pretty tricky. But, there’s always a solution!