gotchajavascriptreactMajor
React setState is batched — state isn't updated immediately
Viewed 0 times
React 18+ for automatic batching everywhere
setState batchingstate not updatingstale statefunctional updateflushSyncReact 18 batching
browser
Problem
Calling setState and immediately reading the state variable gives the OLD value. React batches state updates and applies them on the next render, not synchronously.
Solution
Use functional updates or useEffect to read updated state:
// BAD: reading stale state after setState
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
console.log(count); // Still 0!
setCount(count + 1);
console.log(count); // Still 0! Both set to 1
}
// GOOD: functional update for sequential increments
function handleClick() {
setCount(c => c + 1); // 0 → 1
setCount(c => c + 1); // 1 → 2
}
// GOOD: useEffect to react to state changes
useEffect(() => {
console.log('count is now:', count);
}, [count]);
// BAD: reading stale state after setState
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
console.log(count); // Still 0!
setCount(count + 1);
console.log(count); // Still 0! Both set to 1
}
// GOOD: functional update for sequential increments
function handleClick() {
setCount(c => c + 1); // 0 → 1
setCount(c => c + 1); // 1 → 2
}
// GOOD: useEffect to react to state changes
useEffect(() => {
console.log('count is now:', count);
}, [count]);
Why
React batches all setState calls within an event handler and applies them at once before re-rendering. This is a performance optimization — it prevents unnecessary intermediate renders. In React 18+, batching also applies to async callbacks, timeouts, and promises.
Gotchas
- React 18 batches ALL setState calls (even in setTimeout/promises)
- React 17 only batched in event handlers — async code was NOT batched
- flushSync() forces immediate re-render if absolutely needed (rare)
- Class component this.setState has the same batching behavior
Code Snippets
Functional state updates
// Wrong: both read count=0
setCount(count + 1); // sets to 1
setCount(count + 1); // sets to 1 (not 2!)
// Right: functional update chains correctly
setCount(c => c + 1); // 0 → 1
setCount(c => c + 1); // 1 → 2Context
When reading state immediately after calling setState in React
Revisions (0)
No revisions yet.