ProgramGuru

State as a Snapshot

State as a Snapshot

A teacher increments a counter and then checks the variable in the same handler. It still contains the old value. Understanding why prevents incorrect announcements and predictions about the interface.

Each render sees a snapshot of state. The handlers created during that render close over that snapshot. A setter requests a later render but does not rewrite the current render’s variables or the functions already created from them.

  1. Explain

    Explain why a setter does not rewrite the current handler’s variables.

  2. Trace

    Predict the old handler value and the new displayed count.

  3. Apply

    Calculate a next mark explicitly when the same handler must announce it.

State Snapshots: follow the relationship

  1. Current snapshot: 0

    The count and handler were created for this render.

  2. Request next count: 1

    Calling the setter leaves the handler’s count at 0.

  3. Next render: 1

    The next screen reads 1; the recorded handler message still reports 0.

Read this relationship alongside the complete code. This diagram explains the flow; it does not execute React.

Build the mental model

Imagine a printed attendance sheet for one moment. Requesting an updated sheet does not change the numbers on the sheet in your hand. Likewise, the next render gets the new count while the current handler keeps reading the snapshot that created it.

A handler remembers its render

Snapshot behavior also applies to props and local calculations captured by a handler. A delayed attendance notification created when count is zero still refers to that render’s zero unless the design deliberately reads a separate current-value mechanism. Time passing alone does not change a closure’s captured bindings.

When an operation must use a freshly requested value, name the calculation explicitly as the practice does. When it must update based on whatever value is pending in the state queue, supply a functional updater. These solve different problems and should not be treated as interchangeable magic fixes.

Read the complete React example

The example captures the old count in a status message while requesting the next count. After the click, the screen displays a new count of 1 and a message saying the handler saw 0. These values describe different moments rather than a failed update.

React · JavaScript with JSX

import React, { useState } from 'react';
export default function App() {
  const [count, setCount] = useState(0);
  const [message, setMessage] = useState('No click yet');
  function add() {
    setCount(count + 1);
    setMessage('Handler saw: ' + count);
  }
  return <main><p>Count: {count}</p><p>{message}</p><button onClick={add}>Add arrival</button></main>;
}

Expected screen and behavior

Initial screen: Count: 0 No click yet Add arrival
After selecting Add arrival: the screen includes Handler saw: 0.

Connect the source to the screen

React · main example

import React, { useState } from 'react';
export default function App() {
  const [count, setCount] = useState(0);
  const [message, setMessage] = useState('No click yet');
  function add() {
    setCount(count + 1);
    setMessage('Handler saw: ' + count);
  }
  return <main><p>Count: {count}</p><p>{message}</p><button onClick={add}>Add arrival</button></main>;
}

React renders after processing the update request. The next count and the message are both state values chosen by the handler. An asynchronous callback also retains the values from the render where it was created; waiting does not turn them into live references.

Avoid the common mistakes

Do not rely on reading count immediately after setCount to obtain the requested next count. Calculate nextCount in a local variable when the same handler needs it. When a later operation needs the latest queued value, a functional updater can express that dependency.

Practice before reading the solution

React · starting example

import React, { useState } from 'react';
export default function App() {
  const [count, setCount] = useState(0);
  const [message, setMessage] = useState('No click yet');
  function add() {
    setCount(count + 1);
    setMessage('Handler saw: ' + count);
  }
  return <main><p>Count: {count}</p><p>{message}</p><button onClick={add}>Add arrival</button></main>;
}

Increment a mark and announce the planned next mark accurately.

Predict the initial visible text and explain which values determine it. Then inspect the worked solution below. Change one relevant input in your local project and check whether the screen follows your prediction. A working screen should be checked for the intended meaning, not merely the absence of an error.

Worked practice solution

The practice names nextMark before making either update request. The announcement uses that calculated value, so it agrees with the new screen. This does not change snapshot behavior; it avoids asking the old snapshot to act like a mutable live variable.

React · JavaScript with JSX

import React, { useState } from 'react';
export default function App() {
  const [mark, setMark] = useState(40);
  const [message, setMessage] = useState('No correction yet');
  function correct() {
    const nextMark = mark + 1;
    setMark(nextMark);
    setMessage('Next mark: ' + nextMark);
  }
  return <main><p>Mark: {mark}</p><p>{message}</p><button onClick={correct}>Correct mark</button></main>;
}

Expected screen and behavior

Initial screen: Mark: 40 No correction yet Correct mark
After selecting Correct mark: the screen includes Next mark: 41.

Check your reasoning

React · main example

import React, { useState } from 'react';
export default function App() {
  const [count, setCount] = useState(0);
  const [message, setMessage] = useState('No click yet');
  function add() {
    setCount(count + 1);
    setMessage('Handler saw: ' + count);
  }
  return <main><p>Count: {count}</p><p>{message}</p><button onClick={add}>Add arrival</button></main>;
}

Choose the explanation that matches React and JavaScript behavior. Review the reasoning for each alternative before moving on.

During the first Add arrival handler, what count does the message read?
Choose one answer. You can try again.

Answer and explanation

0, from the render that created that handler

Correct. The requested update becomes visible in a later render.

Use the idea in the next lesson

Each render sees a snapshot of state. The handlers created during that render close over that snapshot. A setter requests a later render but does not rewrite the current render’s variables or the functions already created from them.

Continue with Queueing State Updates. Revisit useState: Component Memory when the relationship between inputs and the resulting screen needs another look.

Read the primary reference: React documentation on State as a Snapshot.

Reaching the recap and completing the quizzes are separate milestones.

    Next lesson: Queueing State Updates →