What is a stale closure in a React effect, and how do you fix it?
A closure captures the values from the render where it was created. An effect with a missing dependency keeps using old values. Fix it by listing the dependency, using the updater form of setState, or storing the latest value in a ref.
Explain it simply
Each render has its own copy of props and state, like a separate photocopy of a form. An effect created in render 1 holds the photocopy of render 1. If the effect never re-runs, it keeps reading old data.
The classic example is a timer that always logs 0.
Example
function Counter() {
const [n, setN] = useState(0);
useEffect(() => {
const id = setInterval(() => setN(n + 1), 1000); // n is always 0 here
return () => clearInterval(id);
}, []); // so n stays 1
// Fix: no dependency on n needed
useEffect(() => {
const id = setInterval(() => setN(x => x + 1), 1000);
return () => clearInterval(id);
}, []);
return <p>{n}</p>;
}What to say in the interview
"A stale closure happens when a callback or effect captures old render values. I fix it by adding the real dependencies, using functional updates when I only need the previous state, or reading the latest value from a ref when I need a stable callback."
Common mistakes
- Silencing the exhaustive-deps warning instead of fixing the closure.