Stale Closures
RE · Common Mistakessyntax
// PROBLEM: handler captures old state value
// FIX: use updater function or refexample
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.