A component's memory

Give a show card memory with useState, so Watched stays after the click.

A click can log or alert. The screen still forgets that click when the handler finishes.

A variable is forgotten

A let inside the card really does change. React never hears about it, so the badge never appears.

import "./styles.css";
import ShowCard from "./ShowCard";

export default function App() {
  return (
    <div className="page">
      <ShowCard
        title="The Bear"
        year={2022}
        genre="Drama"
        rating={8.6}
        still="still-warm"
      />
    </div>
  );
}

Press Watched. The console reads watched is now true. The still has no pill. The next time React draws this card, let watched = false runs again.

useState remembers

Hooks are functions that let a component use React features. useState is the one that remembers a value.

You unpack the pair with array destructuring:

const [watched, setWatched] = useState(false);

watched is this render's value. setWatched asks React for another render. The argument to useState is the first-render value only.

import "./styles.css";
import ShowCard from "./ShowCard";

export default function App() {
  return (
    <div className="page">
      <ShowCard
        title="The Bear"
        year={2022}
        genre="Drama"
        rating={8.6}
        still="still-warm"
      />
    </div>
  );
}

Press Watched. A white pill stays on the still. React remembered true and drew the card again.

Each card has its own memory

Each call to useState belongs to one card. Marking The Bear does not mark Andor.

import "./styles.css";
import ShowCard from "./ShowCard";

export default function App() {
  return (
    <div className="page">
      <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>
  );
}

Press Watched on The Bear. Only that still gets the pill.

Next show

A second piece of memory can walk our three shows. index starts at 0. Next adds one, then wraps back to The Bear.

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

const shows = [
  { title: "The Bear", year: 2022, genre: "Drama", rating: 8.6, still: "still-warm" },
  { title: "Severance", year: 2022, genre: "Thriller", rating: 8.7, still: "still-cold" },
  { title: "Andor", year: 2022, genre: "Sci-Fi", rating: 8.4, still: "still-dusty" },
];

export default function App() {
  const [index, setIndex] = useState(0);
  const show = shows[index];

  function handleNext() {
    setIndex((index + 1) % shows.length);
  }

  return (
    <div className="page">
      <ShowCard
        title={show.title}
        year={show.year}
        genre={show.genre}
        rating={show.rating}
        still={show.still}
        onNext={handleNext}
      />
    </div>
  );
}

Press Next. The card becomes Severance, then Andor, then The Bear again. setIndex asks for a new render with a new number. The card reads that number as props.

Common mistakes

  • Assigning a let and expecting the badge. The variable changes; React does not redraw, so put the flag in useState.
  • Calling useState after a condition. Hooks must run in the same order on every render. Custom hooks come much later.
  • Treating the argument as a live default. useState(false) sets the first render only. Later renders read the remembered value.

Exercise

The Watched pill never stays, and Next never leaves The Bear. Both values are ordinary lets.

Fix both. Press Watched: a Watched pill stays on the still. Press Next: the card shows Severance, then Andor, then The Bear again.

Hint: replace each let with a useState pair, and call the setter instead of assigning.

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

const shows = [
  { title: "The Bear", year: 2022, genre: "Drama", rating: 8.6, still: "still-warm" },
  { title: "Severance", year: 2022, genre: "Thriller", rating: 8.7, still: "still-cold" },
  { title: "Andor", year: 2022, genre: "Sci-Fi", rating: 8.4, still: "still-dusty" },
];

export default function App() {
  let watched = false;
  let index = 0;
  const show = shows[index];

  function handleWatched() {
    watched = true;
    console.log("watched is now", watched);
  }

  function handleNext() {
    index = (index + 1) % shows.length;
    console.log("index is now", index);
  }

  return (
    <div className="page">
      <ShowCard
        title={show.title}
        year={show.year}
        genre={show.genre}
        rating={show.rating}
        still={show.still}
        watched={watched}
        onWatched={handleWatched}
        onNext={handleNext}
      />
    </div>
  );
}