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

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.