Multiple setState calls
Short answer
If you call setState (or setCount, setUser, etc.) several times in a row within one event or a synchronous piece of code,
React batches these updates and performs only one re-render.
But!
If you pass setState just a value (not a function),
all calls will use the same old state value,
so they can "get lost".
1. An example of the problem
function Counter() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);
};
return <button onClick={handleClick}>{count}</button>;
}It seems that count should become 3,
but in fact it becomes 1.
Why?
2. What React does
React does not update count instantly - it puts all the updates into a queue:
setCount(count + 1) → "update count from 0 to 1"
setCount(count + 1) → "update count from 0 to 1"
setCount(count + 1) → "update count from 0 to 1"All three updates use the old value count = 0,
so the result after applying them is 1.
3. The correct way - use a functional update
For each call to build on the current value,
you need to pass setState a function that receives the previous state:
function Counter() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(prev => prev + 1);
setCount(prev => prev + 1);
setCount(prev => prev + 1);
};
return <button onClick={handleClick}>{count}</button>;
}Now each call:
1) prev = 0 → 1
2) prev = 1 → 2
3) prev = 2 → 3The result is 3, as expected.
4. Why React does this
Asynchronous updates
React uses batching - it waits for the event to finish (onClick, onChange, etc.)
and applies all accumulated changes in one render,
so it does not re-render the component after every call.
Performance savings
Instead of 3 re-renders React does 1. That is tens of times faster with large component trees.
5. Outside an event (for example, setTimeout)
Sometimes batching does not apply (in older React versions <18) inside asynchronous callbacks, and updates are performed separately:
setTimeout(() => {
setCount(count + 1);
setCount(count + 1);
}, 0);In React 18 and above batching is enabled even for asynchronous code, so now both updates get merged too.
6. What React stores internally
React keeps a state update queue (updateQueue):
[
(prev) => prev + 1,
(prev) => prev + 1,
(prev) => prev + 1
]During render it walks through it:
let newState = oldState;
for (const update of queue) {
newState = update(newState);
}After applying all the updates the component re-renders once.
Summary
| Situation | What happens |
|---|---|
Several setState(value) in a row | Use the old value → result is only from the last one |
Several setState(prev => …) in a row | Work sequentially → correct result |
| All updates within one event | Merge (batching) → 1 re-render |
React <18 (older) in setTimeout | batching does not apply |
| React 18+ | batching works even in async code |
Conclusion
React does not update state immediately - it accumulates changes. If you need to update state based on the previous one, always use the functional form:
javascriptsetState(prev => newValueBasedOn(prev));This guarantees correctness even with several calls in a row.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.