Study notes · 11% of the exam

State, Batching and Immutability

State is a snapshot per render. Setters queue updates that React batches and applies on the next render, so think in queues, closures and immutable copies.

Key points

  1. 1

    A setter never changes the variable in the current render; handlers, timeouts and async code all see the snapshot from the render that created them.

  2. 2

    React processes queued updates in order: a value replaces the pending state, an updater x => ... receives it. Use updaters when the next value depends on the previous one.

  3. 3

    Since React 18, updates are batched automatically everywhere (handlers, timeouts, promises, native listeners). An await splits updates into separate batches; flushSync forces an immediate commit.

  4. 4

    React bails out when the new state is Object.is-equal to the current one, so mutating an object or array and setting it again does nothing. Copy every level on the path to the change.

  5. 5

    useState(fn) is a lazy initializer; useState(fn()) runs fn on every render. State keeps its first value, so copying props into state freezes them.

  6. 6

    Keep state minimal: no contradictory flags, nothing derivable during render, ids instead of copied objects, and normalized data for deep trees. Reset a subtree with a new key.

  7. 7

    Reducers and updaters must be pure: StrictMode calls them twice in development. dispatch is stable and reducers always receive the latest state, which avoids stale closures.

Common traps

  • setN(n + 1) three times adds 1, not 3, and a later value update overrides earlier updaters in the same queue.

  • Mutating nested data inside a spread copy ([...list] then item.done = true) changes objects other state still shares.

  • Results from async handlers can arrive out of order; ignore or abort stale requests instead of trusting the last response.

Test yourself on State, Batching and Immutability

Ten questions, with the answer and explanation after each one.