What should be the dependencies of useEffect Hook?

Viewed 1639

As it's said, the useEffect hook is the place where we do the side-effects related part. I'm having a confusion of what dependencies should be passed in the dependency array of useEffect hook?

The React documentation says

If you use this optimization, make sure the array includes all values from the component scope (such as props and state) that change over time and that are used by the effect. Otherwise, your code will reference stale values from previous renders.

Consider an example:

export default function App() {
  const [count, setCount] = React.useState(0);

React.useEffect(() => {
    console.log("component mounted");
  }, []);


  const update = () => {
    for(let i=0; i<5;i++){
      setCount(count+i)
    }
  };
  return (
    <div>
    {console.log(count)}
      {count}
      <button type="button" onClick={update}>
        Add
      </button>
    </div>
  );
}

Going with the above statement, we should pass the count variable as a dependency to useEffect, it will make the useEffect re run.

But in the above example, not passing the count dependency doesn't create any problem with the UI. There is not stale value. The variable updates as expected, UI re-renders with exact value, logs the current value.

So my question is, why should we pass the count variable as dependency to useEffect. The UI is working correctly?

UPDATE

I know the useEffect callback is triggered everytime when the value in dependency array changes. My question is what should go in to the dependency array? Do we really need to pass state variables?

Thanks in advance.

4 Answers

To demonstrate and understand the issue print the count variable inside

React.useEffect(() => {
    console.log("component updated ", count);
  }, []); // try to add it here

click again the button and see how it logs to the console

EDIT: If you want to get notified by your IDE that you are missing dependencies then use this plugin https://reactjs.org/docs/hooks-rules.html#eslint-plugin

Keep in mind that it will still complain if you want to simulate onMount

If property from second argument in useEffect change - then component will rerender.

If you pass the count -> then component rerender after count change - one time.

React.useEffect(() => {
    console.log("component mounted");
  }, [count]);

Quick answer:

Pass everytime you need to trigger refreshing component.

What should go into the dependency array?

Those things (props/state) that change over time and that are used by the effect.

In the example, the UI works correctly because setState re-renders the component.

But if we do some side-effect like calling an alert on change of count, we have to pass count to the dependency array. This will make sure the callback is called everytime the dependency (count) in our case, changes.

  React.useEffect(() => {
    alert(`Count ${count}`); // call everytime count changes
  }, [count]); // makes the callback run everytime, the count updates. 

useEffect will get called in an infinite loop unless you give it dependencies.

React.useEffect(() => {
    console.log("Looped endlessly");
}); // dependencies parameter missing

Adding an empty dependency list will cause it to get called just once on component did mount

React.useEffect(() => {
    console.log("Called once on component mount");
}, []); // empty dependency list

Add a state to the dependency list to get called when state gets updated

React.useEffect(() => {
    console.log("Called once on component mount and whenever count changes");
    console.log("Count: " + count);
}, [count]); // count as a dependency
Related