Dev.Lib
arrow_backBack to Blogs
LEVEL_022.10June 27, 20261 min read

Stale closure problem

React & Rendering

Closures capture values from render when callback was created — effects and handlers see old state if deps wrong.

Summary

Closures capture values from render when callback was created — effects and handlers see old state if deps wrong. useEffect missing deps → stale fetches; useCallback with empty deps → stale handlers. Functional updates (setState(prev => ...)) avoid stale state reads. ESLint exhaustive-deps catches many; understand why, don't blindly suppress.


Code example

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

  useEffect(() => {
    const id = setInterval(() => {
      setCount(count + 1); // stale — always sees initial count
    }, 1000);
    return () => clearInterval(id);
  }, []); // fix: setCount(c => c + 1) or add [count]

  return count;
}

Key points

  • Closures capture values from render when callback was created — effects and handlers see old state if deps wrong.
  • useEffect missing deps → stale fetches; useCallback with empty deps → stale handlers.
  • Functional updates (setState(prev => ...)) avoid stale state reads.
  • ESLint exhaustive-deps catches many; understand why, don't blindly suppress.

Further reading

Related concepts