State as a snapshot, and batching: why setCount(count + 1) three times adds one
Each render sees a fixed snapshot of state. Learn updater functions, how React queues and batches updates (everywhere since React 18), when to use flushSync, and how to predict the result of several updates.
Comes up in: React, Recruiter screen, Coding
Learn#
Pallavi’s “Add 3 items” button calls setCount(count + 1) three times. The cart shows 1, not 3, and the console.log right after shows the old value.
State is a snapshot: each render gets fixed values, and event handlers created in that render see those values. setCount does not change count in the current code; it queues an update for the next render.
An updater function, setCount(c => c + 1), receives the pending value, so updates build on each other:
import { GlobalRegistrator } from "@happy-dom/global-registrator";
GlobalRegistrator.register(); // a DOM for Node; in a browser you already have one
const { createElement: h, useState } = await import("react");
const { createRoot } = await import("react-dom/client");
const { flushSync } = await import("react-dom");
let renders = 0;
let addThree;
function Cart() {
const [count, setCount] = useState(0);
renders++;
addThree = () => {
setCount(count + 1);
setCount(count + 1);
setCount((c) => c + 1);
console.log("in the handler, count is still", count);
};
return h("p", null, `items: ${count}`);
}
const box = document.createElement("div");
const root = createRoot(box);
flushSync(() => root.render(h(Cart)));
flushSync(() => addThree());
console.log(box.textContent, "| renders:", renders);
setTimeout(addThree, 0);
await new Promise((r) => setTimeout(r, 50));
console.log(box.textContent, "| renders:", renders);
root.unmount();Output (verified)
in the handler, count is still 0 items: 2 | renders: 2 in the handler, count is still 2 items: 4 | renders: 3
Three updates caused one render each time, even inside setTimeout. That is batching: React waits until your code finishes, then renders once.
State
- snapshot: count = 0
Console
empty
Page
empty
This render's snapshot: count = 0. The click handler was created during this render, so it sees 0.
Read the steps as text
- This render's snapshot: count = 0. The click handler was created during this render, so it sees 0.
- setCount(count + 1) queues "replace with 1". setCount(count + 1) again queues "replace with 1".
- setCount(c => c + 1) queues an updater function. The handler logs count: still 0.
- After the handler, React processes the queue in order: 1, 1, then 1 + 1 = 2. One render shows items: 2.
Predict#
Predict first
React 18+: inside a fetch().then callback you call setA(1) and setB(2). How many renders?
Since React 18 with createRoot, batching is automatic in timeouts, promises and native events too. Before React 18, only React event handlers were batched, which is why older answers say “two”.
Interview angle#
Say it like this — about 30 seconds
“setState does not change the variable right away. It queues an update and schedules a render, and the current render keeps its snapshot, so the handler still sees the old value. Calling setCount(count + 1) three times queues the same replacement three times, so it adds one. The updater form, setCount(c => c + 1), works on the pending value, so three of them add three. Since React 18, updates in the same task are batched into one render everywhere. flushSync forces an immediate render when I really need it.”
Follow-up What is automatic batching?
Follow-up What happens when you call setState? Is it synchronous?
Follow-up Why does setCount(count + 1) three times only add one?
Practice#
Guided
Compute what React does with a queue of updates, and how many renders batching produces.
Done when
- Values replace the pending state; functions update it, in order.
- Each task with updates causes one render.
- Each
flushSynccall renders on its own.
Remember#
Try to answer each card before you open it. Then add them to your reviews.
What does a handler see when it reads state after calling setState?
The old value: this render's snapshot.
setCount(c => c + 1) three times in one handler adds how much?
Three.
Since which version does React batch updates in timeouts and promises?
React 18, with createRoot.
What does flushSync do?
Forces React to apply the updates inside it and render immediately.
Go deeper
Four layers. Stop at the one your interview needs; each ends with a quick check.
The core ideaEvery round
State lives inside React, attached to the component; each render reads a copy of it. Closures created in a render keep that render’s values, like the closures in JS.02.03.
Check yourself: Where does React keep state between renders?
Inside React, attached to the component's position in the tree, not in your variables.
How it works underneathSenior rounds
setCount(5); setCount(c => c + 1) gives 6 Source: React: Queueing a series of state updates.Check yourself: In what order does React process queued updates?
In the order they were queued; values replace, functions transform the pending value.
Edge cases and trapsWhere people slip
Updater functions must be pure: in Strict Mode development React calls them twice. Batching applies within one task; two separate clicks are two renders.
Check yourself: Why should updater functions be pure?
In Strict Mode development React calls them twice to catch side effects.
In productionStaff-level answers
flushSync forces an immediate render, for example to scroll to a just-added item. It hurts performance, so use it rarely Source: React: flushSync.Check yourself: When is flushSync justified?
Rarely: when you must read updated DOM right away, such as scrolling to a just-added item.
Finished the five steps? Mark it, and its cards join your reviews.
Last checked 2026-11-01. Spot a mistake? Use “Report an error” below — fixes are listed on the corrections page.
Interview questions this answers
- What is automatic batching?
- What happens when you call
setState? Is it synchronous? - Why does
setCount(count + 1)three times only add one?