A stale closure in an effect, and why the value is old

An effect with an empty dependency array captures the first render’s variables permanently, so a timer set up once reads values that stopped being current seconds later.

// count is always 0 inside the interval
useEffect(() => {
  const t = setInterval(() => console.log(count), 1000);
  return () => clearInterval(t);
}, []);

// the functional setter reads the current value without a dependency
setCount(c => c + 1);

// or a ref, when the value is only read
const latest = useRef(count);
useEffect(() => { latest.current = count; });

This is the single most confusing thing about hooks for anyone coming from classes, where this.state is always current because this is a mutable object. A closure is a snapshot, and every render creates new ones. The functional setter is the idiomatic fix for updates; the ref is the escape hatch for reads, and reaching for the ref first tends to produce code that works and cannot be reasoned about.