Today I Learned

A Zero One initiative

Beware of Stale Closure Data in React

Something we continually take for granted is that setState is actually an asynchronous method, and closures in JavaScript can get end up getting a stale version of state (if they’re using any) because of this.

This is particularly painful when your next version of some state depends on the previous version (Eg. A counter + 1).

To ensure that closures don’t experience issues like this, you can provide a callback to setState instead of a fixed value.

Eg. Instead of:

[count, setCount] = useState(0);
const increment = () => setCount(count + 1);

Do this:

[count, setCount] = useState(0);
const increment = () => setCount((prevCount) => prevCount + 1);

Your increment closure is now a little more resilient to asynchronous events, and since it doesn’t refer to some stale version of state, it will update the next version accordingly.