How to Fix Stale Closure in React: Simple Solutions
A stale closure in React happens when a function captures outdated variables from its surrounding scope. To fix it, use
useRef to hold the latest value or update functions inside useEffect so they always access current state.Why This Happens
In React, functions inside components capture variables from when they were created. If state changes later, those functions still remember the old values, causing unexpected behavior called a stale closure.
jsx
import React, { useState } from 'react'; function Counter() { const [count, setCount] = useState(0); function showAlert() { setTimeout(() => { alert(`Count is: ${count}`); // Always shows initial count }, 3000); } return ( <div> <p>Count: {count}</p> <button onClick={() => setCount(count + 1)}>Increment</button> <button onClick={showAlert}>Show Alert</button> </div> ); }
Output
When clicking 'Show Alert' after incrementing, alert shows the old count value, not the updated one.
The Fix
Use useRef to keep the latest count value accessible inside the timeout callback. Update the ref whenever count changes so the callback reads fresh data.
jsx
import React, { useState, useRef, useEffect } from 'react'; function Counter() { const [count, setCount] = useState(0); const countRef = useRef(count); useEffect(() => { countRef.current = count; // Keep ref updated }, [count]); function showAlert() { setTimeout(() => { alert(`Count is: ${countRef.current}`); // Shows latest count }, 3000); } return ( <div> <p>Count: {count}</p> <button onClick={() => setCount(count + 1)}>Increment</button> <button onClick={showAlert}>Show Alert</button> </div> ); }
Output
Alert shows the current count value even if it changed after clicking 'Show Alert'.
Prevention
To avoid stale closures:
- Use
useRefto store values that change but should be read fresh inside callbacks. - Define functions inside
useEffector update them when dependencies change. - Use functional updates with
setStatewhen new state depends on old state. - Enable React hooks lint rules to warn about missing dependencies.
Related Errors
Other common React issues related to stale data include:
- Missing dependencies in useEffect: Effects not updating because dependencies are not listed.
- Incorrect state updates: Using old state values instead of functional updates.
- Event handlers with stale props: Handlers capturing old props causing UI bugs.
Key Takeaways
Stale closures happen when functions capture outdated variables in React components.
Use useRef to keep a mutable reference to the latest value accessible inside callbacks.
Update refs or functions inside useEffect to ensure they see current state.
Use functional state updates to avoid relying on stale state values.
Enable React hooks linting to catch missing dependencies and prevent stale closures.