Ever wondered why this sequence only bumps your counter by 1, not 2?
setCount(count + 1);
setCount(count + 1);
console.log(count); // still the old value!
It can feel like React’s hiding some magic, so let’s unpack what’s really happening.
1️⃣ Batched & Deferred Updates
React queues all state updates that happen inside the same event handler, then applies them in one render pass. That means:
- You enqueue your changes immediately, but
- The actual UI update happens after your function finishes.
So you don’t see the new state right away, and that’s why calling setCount
twice doesn’t give you two separate renders.
2️⃣ Immediate Enqueue (No Hidden Timers)
Each call to setCount(...)
runs synchronously. There’s no setTimeout
under the hood. React simply adds your update to an internal queue. As soon as you invoke setCount
, React remembers “we need to update state” and waits for the right moment to flush.
3️⃣ Same Snapshot, Same Result
Inside one render cycle, count
stays at its initial value (say, 0
). Both of your updates see that same 0
and each calculate “0 + 1.” When React finally processes the queue, it applies two identical updates, ending up at 1, not 2.
4️⃣ The Reliable Fix: Functional Updates
Whenever you need to base a new state on the previous value, use the functional form:
setCount(c => c + 1);
setCount(c => c + 1);
Here, each updater receives the latest pending state (c
), so you reliably end up with +2.
💡 What to remember:
- “Async” feel = React batches + defers renders
- “Sync” reality = setter calls enqueue instantly
- Pro tip: Always prefer functional updaters when relying on the previous state
🔗 Reference:
Queueing a Series of State Updates
Have you run into this gotcha? Share your experience or other React surprises below! 👇
Happy coding! 🚀
Top comments (0)