let count = 0;
const log = [];
// Simulates useEffect(() => { setInterval... }, []) — missing count in deps
const savedCount = count; // closure captures 0 forever
const interval = setInterval(() => {
log.push(savedCount); // always 0 — stale!
}, 0);
count = 1;
count = 2;
count = 3;
clearInterval(interval);
setTimeout(() => console.log(log[0]), 10);let count = 0;
const log = [];
// Ref pattern — always reads latest value
const countRef = { current: 0 };
const interval = setInterval(() => {
log.push(countRef.current); // reads latest
}, 0);
count = 1; countRef.current = 1;
count = 2; countRef.current = 2;
count = 3; countRef.current = 3;
clearInterval(interval);
setTimeout(() => console.log(log[0]), 10);Bug: savedCount captures count's value (0) at creation time. Subsequent assignments to count don't affect the closed-over value.
Explanation: countRef.current is a property of a stable object — reading it at tick time always gives the latest value, not the value captured at creation.
Key Insight: Use useRef to store values you want setInterval/setTimeout to read as latest. The ref pattern: ref.current = value in useEffect, read ref.current inside the interval callback.