Stale closure

2 snippets in React

REReact

Dependency Arrays

RE · useMemo & useCallback
Syntax
// All values from component scope used inside must be listed
useCallback(() => fn(a, b), [a, b]);
useMemo(() => compute(x), [x]);
Example
function ChatRoom({ roomId, serverUrl }) {
  // Recreated only when roomId or serverUrl change
  const connect = useCallback(() => {
    const ws = new WebSocket(`${serverUrl}/rooms/${roomId}`);
    ws.onopen = () => console.log('Connected to', roomId);
    return ws;
  }, [roomId, serverUrl]);

  useEffect(() => {
    const ws = connect();
    return () => ws.close();
  }, [connect]);

  return <div>Chat: {roomId}</div>;
}

Note Missing a dependency leads to stale closures. Including an unnecessary one causes pointless recalculations. Use the exhaustive-deps lint rule (eslint-plugin-react-hooks) to catch mistakes automatically.

Stale Closures

RE · Common Mistakes
Syntax
// PROBLEM: handler captures old state value
// FIX: use updater function or ref
Example
function BrokenCounter() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const id = setInterval(() => {
      // BUG: count is always 0 (captured from initial render)
      setCount(count + 1);
    }, 1000);
    return () => clearInterval(id);
  }, []); // empty deps = closure captures initial count

  return <p>{count}</p>; // stuck at 1
}

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

  useEffect(() => {
    const id = setInterval(() => {
      // FIX: updater function always has the latest value
      setCount(prev => prev + 1);
    }, 1000);
    return () => clearInterval(id);
  }, []);

  return <p>{count}</p>; // increments correctly
}

Note Stale closures happen when a callback captures a state value that never updates. Common in setInterval, setTimeout, and event listeners set up in useEffect. Use the updater form of setState or store values in a ref.

Frequently asked questions

How does React handle stale closure?
React covers this with 2 copy-ready snippets on this page. The "Dependency Arrays" snippet in React uses `// All values from component scope used inside must be listed`.
Which code does the React example use?
The "Dependency Arrays" snippet uses `// All values from component scope used inside must be listed`, from the useMemo & useCallback section of the React cheat sheet.
What other React snippets are shown for "stale closure"?
Besides "Dependency Arrays", this page also shows "Stale Closures".
Is there anything to watch out for?
Yes. For "Dependency Arrays": Missing a dependency leads to stale closures. Including an unnecessary one causes pointless recalculations. Use the exhaustive-deps lint rule (eslint-plugin-react-hooks) to catch mistakes automatically.