Impact: MEDIUM (prevents silent data corruption from outdated state references in async operations)
JavaScript closures capture variables at creation time. When a callback inside useEffect, setInterval, setTimeout, or addEventListener references a state variable, it captures the value from that specific render — not the current value. This causes the callback to silently use outdated data.
Stale closures are the most frequently misdiagnosed React bug. They manifest as "my state isn't updating" or "my handler uses the old value."
Incorrect (stale closure captures initial state):
'use client'// ❌ count is always 0 inside the interval — captured from the first renderfunction Counter() { const [count, setCount] = useState(0) useEffect(() => { const id = setInterval(() => { console.log(count) // Always logs 0! setCount(count + 1) // Always sets to 1! }, 1000) return () => clearInterval(id) }, []) // Empty deps = closure captures initial count forever return <span>{count}</span>}