Introduction
If you call setState (or setCount) and immediately console.log, you’ll often see the old value. That’s not a bug. React deliberately makes state updates asynchronous and batched to avoid extra renders. This guide explains why that happens and how to read the updated state correctly.
Why doesn’t state change immediately?
setStatedoesn’t change the value on the spot; it enqueues an update request.- JavaScript keeps running, and React applies the new state on the next render.
- Any code running right after
setStatestill sees the previous value.
Behind the scenes:
setCountis called.- React puts the update in a queue.
- The rest of your JS keeps executing.
- React re-renders and the new state becomes available.
Basic example
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
console.log(count); // still the previous value
};
console.log runs before the next render, so it logs the old state.
How to read the fresh value
Use useEffect
useEffect(() => {
console.log(count); // always the updated value
}, [count]);
useEffect runs after render, so the logged value is current.
Use a Functional Update
When the new value depends on the previous one, the functional form always has the latest state:
setCount(prev => {
const next = prev + 1;
console.log(next); // the new value is here
return next;
});
Back-to-back updates
Common mistake:
setCount(count + 1);
setCount(count + 1); // both use the stale value
Result is only +1. Correct version:
setCount(prev => prev + 1);
setCount(prev => prev + 1); // total +2
Functional updates ensure each call uses the latest state.
Where you need to be careful
- Logging immediately after
setState - Sending state to an API right after updating
- Branching logic that uses the state in the same tick
- Multiple sequential updates on one state without the functional form
Use useEffect or functional updates in these cases.
Quick checklist
- Does the update depend on the previous value? → Use the functional form.
- Need the fresh value? → Log inside a
useEffectthat depends on that state. - Doing multiple updates in a row? → Make them all functional so batching works.
Wrap-up
Seeing the old value after setState is expected: React queues and batches updates. To get the new state, either wait for the next render (useEffect) or use the functional updater. Rule of thumb: “If you need the previous state, use a function; if you need the new state, read it after render.”
