Navigate
Search topics, hooks, and fundamentals
Every React render creates a new closure. This is the reason dependency arrays exist — and why forgetting a dependency causes stale bugs.
When React renders your component, it calls the function. All the variables, effects, and handlers created during that call form a closure — a snapshot of that render's values:
function Counter() {
const [count, setCount] = useState(0);
// Render 1: count = 0
// Render 2: count = 1
// Render 3: count = 2
// Each render creates a NEW closure with its own 'count'
useEffect(() => {
// This effect closes over the 'count' from THIS render
document.title = `Count: ${count}`;
}, [count]); // dependency array tells React when to re-run
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}count = 0closure captures count=0The dependency array tells React: "re-run this effect when these values change." Without it, the effect captures stale values from the first render and never updates:
useEffect(() => {
// This function is a closure over 'count'
const id = setInterval(() => {
console.log(count); // Which count? The one from THIS render
}, 1000);
return () => clearInterval(id);
}, [count]); // Re-create when count changes
// Without [count], this would be a stale closure!
// The effect would always see count from the first render.Forgetting a dependency means your effect runs with an old closure. Try this interactive example — notice how the missing dependency causes the effect to read a stale value: