Queueing updates

Queue several watched-count updates, and use an updater when the next value depends on the last.

+3 only adds one

Each setWatchedCount(watchedCount + 1) reads this render's number. Three calls in one click all compute 0 + 1.

import { useState } from "react";
import "./styles.css";
import ShowCard from "./ShowCard";

export default function App() {
  const [watchedCount, setWatchedCount] = useState(0);

  function handlePlusOne() {
    setWatchedCount(watchedCount + 1);
  }

  function handlePlusThree() {
    setWatchedCount(watchedCount + 1);
    setWatchedCount(watchedCount + 1);
    setWatchedCount(watchedCount + 1);
  }

  return (
    <div className="page">
      <div className="toolbar">
        <p className="count">
          Shows watched: {watchedCount}
        </p>
        <button onClick={handlePlusOne} className="card-button">+1</button>
        <button onClick={handlePlusThree} className="card-button">+3</button>
      </div>
      <div className="cards">
        <ShowCard
          title="The Bear"
          year={2022}
          genre="Drama"
          rating={8.6}
          still="still-warm"
        />
        <ShowCard
          title="Severance"
          year={2022}
          genre="Thriller"
          rating={8.7}
          still="still-cold"
        />
        <ShowCard
          title="Andor"
          year={2022}
          genre="Sci-Fi"
          rating={8.4}
          still="still-dusty"
        />
      </div>
    </div>
  );
}

Press +3. The heading goes from Shows watched: 0 to Shows watched: 1. That is the snapshot lesson applied three times.

The updater function

Pass a function when the next value depends on the last one. React applies each updater in order.

setWatchedCount((count) => count + 1);

The arrow is the same shape as Arrow Functions and this.

import { useState } from "react";
import "./styles.css";
import ShowCard from "./ShowCard";

export default function App() {
  const [watchedCount, setWatchedCount] = useState(0);

  function handlePlusOne() {
    setWatchedCount(watchedCount + 1);
  }

  function handlePlusThree() {
    setWatchedCount((count) => count + 1);
    setWatchedCount((count) => count + 1);
    setWatchedCount((count) => count + 1);
  }

  return (
    <div className="page">
      <div className="toolbar">
        <p className="count">
          Shows watched: {watchedCount}
        </p>
        <button onClick={handlePlusOne} className="card-button">+1</button>
        <button onClick={handlePlusThree} className="card-button">+3</button>
      </div>
      <div className="cards">
        <ShowCard
          title="The Bear"
          year={2022}
          genre="Drama"
          rating={8.6}
          still="still-warm"
        />
        <ShowCard
          title="Severance"
          year={2022}
          genre="Thriller"
          rating={8.7}
          still="still-cold"
        />
        <ShowCard
          title="Andor"
          year={2022}
          genre="Sci-Fi"
          rating={8.4}
          still="still-dusty"
        />
      </div>
    </div>
  );
}

Press +3. The heading goes from Shows watched: 0 to Shows watched: 3. Reach for the updater when the next number depends on the previous one.

Common mistakes

  • setWatchedCount(watchedCount + 1) three times. All three read the same snapshot, so the heading moves by one.
  • An updater for a known value. setWatched(true) stays a value. You already know the next flag.
  • Forgetting the function form. (count) => count + 1 receives the latest queued number, not this render's.

Exercise

+1 works. +3 still adds one, because it repeats watchedCount + 1.

Fix +3. One click takes the heading from Shows watched: 0 to Shows watched: 3. The three cards stay on screen.

Hint: pass (count) => count + 1 three times, not watchedCount + 1.

import { useState } from "react";
import "./styles.css";
import ShowCard from "./ShowCard";

export default function App() {
  const [watchedCount, setWatchedCount] = useState(0);

  function handlePlusOne() {
    setWatchedCount(watchedCount + 1);
  }

  function handlePlusThree() {
    setWatchedCount(watchedCount + 1);
    setWatchedCount(watchedCount + 1);
    setWatchedCount(watchedCount + 1);
  }

  return (
    <div className="page">
      <div className="toolbar">
        <p className="count">
          Shows watched: {watchedCount}
        </p>
        <button onClick={handlePlusOne} className="card-button">+1</button>
        <button onClick={handlePlusThree} className="card-button">+3</button>
      </div>
      <div className="cards">
        <ShowCard
          title="The Bear"
          year={2022}
          genre="Drama"
          rating={8.6}
          still="still-warm"
        />
        <ShowCard
          title="Severance"
          year={2022}
          genre="Thriller"
          rating={8.7}
          still="still-cold"
        />
        <ShowCard
          title="Andor"
          year={2022}
          genre="Sci-Fi"
          rating={8.4}
          still="still-dusty"
        />
      </div>
    </div>
  );
}