ProgramGuru

Queueing State Updates

Queueing State Updates

One action awards three participation points. Calling the setter three times with score + 1 looks reasonable, but every expression uses the same render’s score. We need to express three successive changes rather than three identical replacements.

React queues state updates and normally processes event-handler updates together. A functional updater receives the pending state from the queue, returns the next value and allows later updaters to build on that result.

  1. Explain

    Trace three updater functions from zero to three points.

  2. Trace

    Distinguish replacing state from transforming a queued value.

  3. Apply

    Keep an updater pure so React can safely evaluate it again.

Queued Updates: follow the relationship

  1. Start: 0 points

    React starts processing from the current stored value.

  2. Apply queued updaters

    Three pure n => n + 1 steps produce 1, then 2, then 3.

  3. Next screen: 3 points

    React displays the final result after processing the updates.

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

Build the mental model

Distinguish a replacement value from an update rule. setScore(score + 1) calculates a number immediately from the current snapshot. setScore(previous => previous + 1) supplies a rule to apply to the queued value. Repeating these forms has different consequences.

Trace the queue on paper

Write each queued item as either replace with a value or calculate from the pending value. Start with the current state and apply the queue in order. This small trace explains mixed updates more reliably than counting setter calls. A replacement discards the previously accumulated pending result at that point.

Do not assume each setter creates an intermediate screen that a user will see. The important observable result is the committed interface after React processes the update. If an application genuinely requires sequential visible phases, express those phases as intentional state and interactions rather than relying on setter timing.

Read the complete React example

The example starts at zero and queues three updater functions. Their inputs are zero, one and two, so the final score is three. The practice first queues a replacement of five and then an updater that adds two, giving seven.

React · JavaScript with JSX

import React, { useState } from 'react';
export default function App() {
  const [score, setScore] = useState(0);
  function award() {
    setScore(previous => previous + 1);
    setScore(previous => previous + 1);
    setScore(previous => previous + 1);
  }
  return <main><p>Points: {score}</p><button onClick={award}>Award three points</button></main>;
}

Expected screen and behavior

Initial screen: Points: 0 Award three points
After selecting Award three points: the screen includes Points: 3.

Connect the source to the screen

React · main example

import React, { useState } from 'react';
export default function App() {
  const [score, setScore] = useState(0);
  function award() {
    setScore(previous => previous + 1);
    setScore(previous => previous + 1);
    setScore(previous => previous + 1);
  }
  return <main><p>Points: {score}</p><button onClick={award}>Award three points</button></main>;
}

React waits until the handler finishes before applying the grouped interface update. The queue describes the pending value, not a sequence of three immediately visible screens. Separate intentional clicks are separate events; batching does not mean all future clicks share one permanent snapshot.

Follow each queued calculation

React · three queued updaters

import React, { useState } from 'react';
export default function App() {
  const [score, setScore] = useState(0);
  function award() {
    setScore(previous => previous + 1);
    setScore(previous => previous + 1);
    setScore(previous => previous + 1);
  }
  return <main><p>Points: {score}</p><button onClick={award}>Award three points</button></main>;
}

The current score is 0. One click queues three updater functions; each receives the result of the preceding update rather than rereading the handler’s original score. The first computes 0 + 1 = 1, the second computes 1 + 1 = 2, and the third computes 2 + 1 = 3. The next screen therefore displays Points: 3.

If all three calls instead passed score + 1, each expression in this handler would read the same snapshot of 0 and request the replacement value 1. Three replacement requests for 1 do not add up to 3. The number of setter calls alone does not determine the final value; what each queued request means matters.

On another click, the stored score starts at 3 and the three updater functions produce 4, 5 and 6. Keep those functions pure: sending a notification inside an updater would mix a calculation with an external action that React might evaluate again.

Avoid the common mistakes

Keep updater functions pure. Do not send a network request, alert the user or mutate records inside an updater. React may evaluate them more than once in development checks. Use the event handler for event-related side effects and the updater for calculating state.

Practice before reading the solution

React · starting example

import React, { useState } from 'react';
export default function App() {
  const [score, setScore] = useState(0);
  function award() {
    setScore(previous => previous + 1);
    setScore(previous => previous + 1);
    setScore(previous => previous + 1);
  }
  return <main><p>Points: {score}</p><button onClick={award}>Award three points</button></main>;
}

Reset bonus points to five and add two in the same action.

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 solution intentionally mixes a replacement and an updater to demonstrate ordering. The updater sees the five queued before it rather than the earlier zero. Reversing those two calls would make the final replacement win and produce five.

React · JavaScript with JSX

import React, { useState } from 'react';
export default function App() {
  const [bonus, setBonus] = useState(0);
  function award() {
    setBonus(5);
    setBonus(previous => previous + 2);
  }
  return <main><p>Bonus: {bonus}</p><button onClick={award}>Set and add bonus</button></main>;
}

Expected screen and behavior

Initial screen: Bonus: 0 Set and add bonus
After selecting Set and add bonus: the screen includes Bonus: 7.

Check your reasoning

React · main example

import React, { useState } from 'react';
export default function App() {
  const [score, setScore] = useState(0);
  function award() {
    setScore(previous => previous + 1);
    setScore(previous => previous + 1);
    setScore(previous => previous + 1);
  }
  return <main><p>Points: {score}</p><button onClick={award}>Award three points</button></main>;
}

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

Starting at zero, three setScore(score + 1) calls in one handler normally produce which score?
Choose one answer. You can try again.

Answer and explanation

1

Correct. Each expression calculates the same replacement value from the zero snapshot.

Use the idea in the next lesson

React queues state updates and normally processes event-handler updates together. A functional updater receives the pending state from the queue, returns the next value and allows later updaters to build on that result.

Continue with Updating Objects in State. Revisit State as a Snapshot when the relationship between inputs and the resulting screen needs another look.

Read the primary reference: React documentation on Queueing State Updates.

Reaching the recap and completing the quizzes are separate milestones.

    Next lesson: Updating Objects in State →