State is a snapshot
React chapter 138 · useState · the update queue · batching · source: react.dev/learn/state-as-a-snapshot
The problem
A “+3” button calls setScore(score + 1) three times and the score goes up by one. Inside one render, score is a fixed number, so all three calls ask for the same value.
The pattern that fixes it
Pass an updater: setScore(s => s + 1). React queues the functions and runs them in order on the next render, each getting the result of the one before. Three updates, one render, +3.
In plain words
Each render gets its own copy of the state, frozen like a photo. Calling setScore does not change the photo you are holding; it asks React for a new photo next render.
Three people each read “score: 0” off the same whiteboard photo and each write “make it 1”. Passing a note that says “add one to whatever it is now” works no matter how many people do it.
Terms
- snapshot
- The state values a render was called with. Event handlers created in that render see those values forever.
- update queue
- React stores every setState call and applies them, in order, during the next render.
- batching
- Several updates in one event produce one re-render, not one per call.
Going deeper
Value vs updater, precisely
setScore(5) queues “replace with 5”. setScore(s => s + 1) queues a function. During the next render React folds the queue left to right: 0 → replace 1 → replace 1 is 1, 0 → +1 → +1 → +1 is 3. Use the updater whenever the next value depends on the previous one.
Stale closures
A setTimeout, promise or subscription callback created in render N keeps render N’s snapshot. Three clicks within two seconds each schedule setScore(score + 1) with the same old score. Updaters, refs for “latest value”, or useEffectEvent (chapter 15) fix the different shapes of this bug.
Automatic batching (React 18+)
Updates are batched everywhere: event handlers, timeouts, promises, native listeners. flushSync(() => set(x)) forces a synchronous render when you must read the DOM right after, for example to scroll to a new item. It is an escape hatch, not a habit.
Keep learning
- Previous: Render, then commit
- Next: Effects: sync, then clean up
- Related: Render, then commit
- Related: Effects: sync, then clean up
- Related: Identity: position, type, key
- Related: Lift state up
- Related: useRef: remember without re-rendering
More courses
- Jobs, linked to lessons — Browse jobs where every requirement links to the chapter that teaches it. See how ready you are, how long the gap takes to close, and which listings look stale.
- Match a job post to courses — Paste a job post. Each skill is matched to a track here and put in learning order, foundations first.
- Database in depth — How a query becomes work. Indexes, isolation, locks, and the plans that explain why a page is slow — not an ORM tour.
- System Design — Eight interview case studies, from a flash sale and Tatkal to a live final and a chat model, plus JWT in separate chapters.
- Microservices — Microservice patterns one diagram at a time: how to split a monolith, how services talk, and how data stays consistent without distributed transactions.
- Kafka — A beginner-to-senior path through event logs, reliable delivery, production operations, and disaster recovery.
- Frontend system design — How a UI is sliced when it is a product, not a page: ownership of state, cache, routes, and what happens when the network lies.
- CSS in depth — See the box, then the page. Visual chapters from the box model through Flexbox surprises, Grid, selectors, and units, then backgrounds, shapes, and visual effects, each with a playground.
- Accessibility — HTML labs first, then a full pass of WAI-ARIA 1.3: naming, every state, widget and structure roles, live regions, ariaNotify, and relationships.
- Docker — What an image is, why layers cache, and how containers find each other on a network.
- Redis — Fifteen Redis patterns, one picture each: cache, session, lock, counter, rate limiter, queue, and when Redis is the wrong tool.