Home > other >  Why would a value get stale using closure in React?
Why would a value get stale using closure in React?

Time:05-02

In the documentation for useEffect() it shows the following example:

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

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });
}

It then says:

Experienced JavaScript developers might notice that the function passed to useEffect is going to be different on every render. This is intentional. In fact, this is what lets us read the count value from inside the effect without worrying about it getting stale. Every time we re-render, we schedule a different effect, replacing the previous one.

I don't understand why count would get stale inside useEffect() since useEffect has count within its closure. Since useEffect() is a hook, shouldn't it always have access to the latest state variable? Also, in general, how can I be sure that I always have the latest value for a state variable?

CodePudding user response:

I don't understand why count would get stale inside useEffect() since useEffect has count within its closure.

Keep in mind that Example is going to be called multiple times, once for every render. So there are N renders, N count variables, and N useEffect functions. So it's true that each effect function has count within its closure, but each of them has a specific count within its closure. Whatever value it had when the closure was created, that's the value it will have when the effect runs. If you only created the function once, closing over the first value of count, then that code would only ever see the first value of count (ie, 0).

Also, in general, how can I be sure that I always have the latest value for a state variable?

If you're setting state, then you can always access the latest value by using the function version of setState. For example:

setCount(prev => /* calculate new state from prev */)`

For other cases, either include the state in the dependency array of your useEffect, so that the effect re-runs when the count changes:

useEffect(() => {
  // some code that uses `count`
}, [count]);

Or leave the dependency array off entirely if you want the effect to run on every render.

useEffect(() => {
  // some code that uses `count`
});
  • Related