Zum Inhalt springen
aviral gupta

// B5.5 · ca. 25 Min. · Einstieg

In React denken

Nach dieser Lektion können Sie einen Entwurf in fünf Schritten in eine React-App verwandeln und entscheiden, was State ist und welche Komponente ihn besitzt.

Lektion 5 von 6 in B5 State verwalten

Danach können Sie

  • Eine Oberfläche in eine Komponentenhierarchie zerlegen und eine statische Version nur mit Props bauen
  • Den minimalen State finden und alles andere daraus berechnen
  • State im nächsten gemeinsamen Elternteil halten und Setter für den umgekehrten Datenfluss nach unten reichen
  1. Aufwärmen · Aufgabe 1 von 7

    Aufwärmen mit Lektion B5.4: Ein key setzt State zurück. Sie klicken zweimal auf den Counter, dann auf Toggle. Was zeigen die Buttons?

    function Counter() {
      const [score, setScore] = useState(0);
      return <button onClick={() => setScore(score + 1)}>Score: {score}</button>;
    }
    
    function App() {
      const [isOn, setIsOn] = useState(false);
      return (
        <div>
          <Counter key={isOn ? 'on' : 'off'} />
          <button onClick={() => setIsOn(!isOn)}>Toggle</button>
        </div>
      );
    }
  2. Vorhersagen · Aufgabe 2 von 7

    Sagen Sie voraus, bevor Sie weiterlesen. Ein Suchfeld hängt am State, aber nichts behandelt das Tippen. Sie tippen blue. Was enthält das Eingabefeld?

    function SearchBar({ query }) {
      return <input aria-label="Search songs" value={query} />;
    }
    
    function Playlist() {
      const [query, setQuery] = useState('');
      return <SearchBar query={query} />;
    }
  3. Üben · Aufgabe 3 von 7

    Eine Playlist-App hat vier Daten: das Songs-Array (als Props übergeben), den Suchtext, die Checkbox Only liked und die Liste der sichtbaren Songs. Welche davon sind State?

  4. Üben · Aufgabe 4 von 7

    Ordnen Sie jedem Schritt von In React denken zu, was Sie darin tun.

  5. Üben · Aufgabe 5 von 7

    Playlist reicht seinen Setter als onQueryChange nach unten. Ergänzen Sie das Ereignis-Attribut, über das das Eingabefeld jede Änderung meldet.

    <input value={query} ____={e => onQueryChange(e.target.value)} />
    <input value={query} ={e => onQueryChange(e.target.value)} />
  6. Denksport · Aufgabe 6 von 7

    Knobelaufgabe. List kopiert die Prop songs in einen eigenen State. Sie klicken einmal auf Add song. Wie viele Einträge zeigt die Liste?

    function List({ songs }) {
      const [items] = useState(songs);
      return (
        <ul>
          {items.map(song => <li key={song}>{song}</li>)}
        </ul>
      );
    }
    
    function App() {
      const [songs, setSongs] = useState(['Blue Hour', 'Salt']);
      return (
        <>
          <List songs={songs} />
          <button onClick={() => setSongs([...songs, 'Night Bus'])}>Add song</button>
        </>
      );
    }
  7. Anwenden · Aufgabe 7 von 7

    Mini-Aufgabe: eine Aufgabenliste mit einer Checkbox "Only open chores". Schreiben Sie in main.jsx eine Komponente Chores, die eine State-Variable onlyOpen besitzt, eine Komponente Filter mit der kontrollierten Checkbox und eine ChoreList, die die Aufgaben rendert, die sie als Props bekommt. Das Array chores hat id, name und ein Flag done. Speichern Sie die gefilterten Aufgaben nicht: Berechnen Sie sie in Chores und reichen Sie sie nach unten.

    Prüfen Sie Ihr Ergebnis anhand dieser Liste

Selbst programmieren

Lesen Sie das ausgearbeitete Beispiel und lösen Sie dann die Übungen. Ihr Code läuft in Ihrem Browser oder auf Ihrem Computer und wird nie hochgeladen.

Ausgearbeitetes Beispiel

Eine filterbare Playlist, Schritt für Schritt

Die Kommentare markieren die fünf Schritte. SongList und SongRow sind der statische Teil: Sie lesen nur Props. Suchtext und Checkbox sind der einzige State, besessen von ihrem gemeinsamen Elternteil, und die gefilterte Liste wird beim Rendern berechnet. Die Setter wandern als onQueryChange und onLikedOnlyChange nach unten. Ein Lauf mountet nur; deshalb tippen und klicken Timer am Ende von main.jsx, alle 50 ms ein Schritt, und geben nach jeder Änderung die Liste aus. Im Browser bleibt die Seite lebendig: Probieren Sie es selbst.

main.jsx

import { useState } from 'react';

const SONGS = [
  { id: 1, title: 'Blue Hour', artist: 'Mira', liked: true },
  { id: 2, title: 'Paper Boats', artist: 'Tom', liked: false },
  { id: 3, title: 'Night Bus', artist: 'Mira', liked: true },
  { id: 4, title: 'Salt', artist: 'Jun', liked: false }
];

// Steps 3 and 4: the minimal state is the search text and the checkbox.
// Both are used by SearchBar and by SongList, so they live in their parent.
export default function FilterablePlaylist() {
  const [query, setQuery] = useState('');
  const [likedOnly, setLikedOnly] = useState(false);

  // Everything else is computed on demand, not stored.
  const shown = SONGS.filter(
    song =>
      song.title.toLowerCase().includes(query.toLowerCase()) &&
      (!likedOnly || song.liked)
  );

  return (
    <div>
      <SearchBar
        query={query}
        likedOnly={likedOnly}
        onQueryChange={setQuery}
        onLikedOnlyChange={setLikedOnly}
      />
      <SongList songs={shown} />
    </div>
  );
}

// Step 5: inverse data flow. The setters come down as props.
function SearchBar({ query, likedOnly, onQueryChange, onLikedOnlyChange }) {
  return (
    <form>
      <input
        aria-label="Search songs"
        value={query}
        onChange={e => onQueryChange(e.target.value)}
      />
      <label>
        <input
          type="checkbox"
          checked={likedOnly}
          onChange={e => onLikedOnlyChange(e.target.checked)}
        />
        Only liked songs
      </label>
    </form>
  );
}

// Step 2: the static parts only read their props.
function SongList({ songs }) {
  return (
    <ul>
      {songs.map(song => <SongRow key={song.id} song={song} />)}
    </ul>
  );
}

function SongRow({ song }) {
  return <li>{song.title}</li>;
}

// A run only mounts. Act like a user, one step every 50 ms.
const search = value => {
  const input = document.querySelector('input');
  const win = input.ownerDocument.defaultView;
  Object.getOwnPropertyDescriptor(Object.getPrototypeOf(input), 'value').set.call(input, value);
  input.dispatchEvent(new win.Event('input', { bubbles: true }));
};
const print = () =>
  console.log([...document.querySelectorAll('li')].map(li => li.textContent).join(', '));
const plan = [
  print,
  () => search('o'),
  print,
  () => document.querySelector('input[type=checkbox]').click(),
  print,
  () => search(''),
  print
];
plan.forEach((step, i) => setTimeout(step, 50 * (i + 1)));

Ausführen mit

npm run dev

Ausgabe

Blue Hour, Paper Boats, Night Bus, Salt
Blue Hour, Paper Boats
Blue Hour
Blue Hour, Night Bus
  • Die Eingabe "o" ließ die zwei Titel mit einem o stehen. Die Liste wurde aus query und SONGS neu berechnet, nicht bearbeitet.
  • Die Checkbox schränkte sie dann auf Blue Hour ein: Beide State-Werte gehen in denselben Filter ein.
  • Das Leeren des Suchtexts ließ die Checkbox wirken: Blue Hour und Night Bus, die gemochten Songs. Nichts musste von Hand zurückgesetzt werden, weil außer den zwei Werten nichts gespeichert war.
  • SearchBar besitzt nichts. Es zeigt seine Props und ruft die Setter auf, die es bekommen hat.
Ändern und ausführen

Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.

Beim ersten Ausführen lädt Ihr Browser React und den Compiler herunter, der JSX in JavaScript übersetzt (bis zu 2 MB), und speichert beides im Cache. Ihre Komponenten laufen in Ihrem Browser und bleiben auf Ihrem Gerät.

Ihre Seite

Ihre Seite läuft in einem abgeschotteten Rahmen. Sie hat keinen Zugriff auf diese Website, und ihr localStorage liegt nur für diesen Lauf im Speicher: Der nächste Lauf beginnt leer.

Führen Sie das Programm aus, um seine Seite hier zu sehen.

Übungen

Übung 1 von 2

Den umgekehrten Datenfluss ergänzen

FilterableSongs besitzt den State und filtert die Liste richtig, aber Sie können weder im Suchfeld tippen noch die Checkbox ankreuzen: SearchBar liest query und likedOnly und kann sie nicht ändern. Ergänzen Sie den fehlenden Schritt 5. Reichen Sie die beiden Setter von FilterableSongs unter den Namen onQueryChange und onLikedOnlyChange nach unten und rufen Sie sie in den onChange-Handlern der Eingabefelder auf. Lassen Sie State und Filter, wie sie sind.

Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.

Beim ersten Ausführen lädt Ihr Browser React und den Compiler herunter, der JSX in JavaScript übersetzt (bis zu 2 MB), und speichert beides im Cache. Ihre Komponenten laufen in Ihrem Browser und bleiben auf Ihrem Gerät.

Ihre Seite

Ihre Seite läuft in einem abgeschotteten Rahmen. Sie hat keinen Zugriff auf diese Website, und ihr localStorage liegt nur für diesen Lauf im Speicher: Der nächste Lauf beginnt leer.

Führen Sie das Programm aus, um seine Seite hier zu sehen.

Hinweise
  1. Hinweis 1

    Ein Kind kann eine Prop nicht ändern. Der Elternteil muss eine Funktion nach unten reichen.

  2. Hinweis 2

    FilterableSongs kann setQuery und setLikedOnly als Props übergeben.

  3. Hinweis 3

    Rufen Sie für das Textfeld onQueryChange(e.target.value) auf, für die Checkbox onLikedOnlyChange(e.target.checked).

Eine Lösung zeigen

Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.

import { useState } from 'react';

const SONGS = [
  { id: 1, title: 'Blue Hour', artist: 'Mira', liked: true },
  { id: 2, title: 'Paper Boats', artist: 'Tom', liked: false },
  { id: 3, title: 'Night Bus', artist: 'Mira', liked: true },
  { id: 4, title: 'Salt', artist: 'Jun', liked: false }
];

export default function FilterableSongs() {
  const [query, setQuery] = useState('');
  const [likedOnly, setLikedOnly] = useState(false);
  const shown = SONGS.filter(
    song =>
      song.title.toLowerCase().includes(query.toLowerCase()) &&
      (!likedOnly || song.liked)
  );
  return (
    <div>
      <SearchBar
        query={query}
        likedOnly={likedOnly}
        onQueryChange={setQuery}
        onLikedOnlyChange={setLikedOnly}
      />
      <SongList songs={shown} />
    </div>
  );
}

function SearchBar({ query, likedOnly, onQueryChange, onLikedOnlyChange }) {
  return (
    <form>
      <input
        aria-label="Search songs"
        value={query}
        onChange={e => onQueryChange(e.target.value)}
      />
      <label>
        <input
          type="checkbox"
          checked={likedOnly}
          onChange={e => onLikedOnlyChange(e.target.checked)}
        />
        Only liked songs
      </label>
    </form>
  );
}

function SongList({ songs }) {
  return (
    <ul>
      {songs.map(song => <li key={song.id}>{song.title}</li>)}
    </ul>
  );
}
Auf dem eigenen Computer ausführen

Installieren Sie React 19.3 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.

main.jsx

import { useState } from 'react';

const SONGS = [
  { id: 1, title: 'Blue Hour', artist: 'Mira', liked: true },
  { id: 2, title: 'Paper Boats', artist: 'Tom', liked: false },
  { id: 3, title: 'Night Bus', artist: 'Mira', liked: true },
  { id: 4, title: 'Salt', artist: 'Jun', liked: false }
];

export default function FilterableSongs() {
  const [query, setQuery] = useState('');
  const [likedOnly, setLikedOnly] = useState(false);
  const shown = SONGS.filter(
    song =>
      song.title.toLowerCase().includes(query.toLowerCase()) &&
      (!likedOnly || song.liked)
  );
  return (
    <div>
      <SearchBar query={query} likedOnly={likedOnly} />
      <SongList songs={shown} />
    </div>
  );
}

function SearchBar({ query, likedOnly }) {
  return (
    <form>
      <input aria-label="Search songs" value={query} />
      <label>
        <input type="checkbox" checked={likedOnly} />
        Only liked songs
      </label>
    </form>
  );
}

function SongList({ songs }) {
  return (
    <ul>
      {songs.map(song => <li key={song.id}>{song.title}</li>)}
    </ul>
  );
}

main.test.jsx

import {test} from 'node:test';
import assert from 'node:assert/strict';
import {render, screen, fireEvent} from '@testing-library/react';
import FilterableSongs from './main.jsx';

const titles = () => screen.queryAllByRole('listitem').map((li) => li.textContent).join(', ');
const search = (value) => fireEvent.change(screen.getByRole('textbox', {name: 'Search songs'}), {target: {value}});
const liked = () => fireEvent.click(screen.getByRole('checkbox', {name: 'Only liked songs'}));
const says = (what) => 'die Liste zeigt ' + JSON.stringify(titles()) + ' ' + what;

test('Eine Eingabe im Suchfeld filtert die Liste', () => {
  render(<FilterableSongs />);
  search('o');
  assert.equal(titles(), 'Blue Hour, Paper Boats', says('nach der Eingabe von "o"'));
});

test('Ein leeres Suchfeld zeigt wieder alle Songs', () => {
  render(<FilterableSongs />);
  search('o');
  search('');
  assert.equal(titles(), 'Blue Hour, Paper Boats, Night Bus, Salt', says('nach dem Leeren des Suchfelds'));
});

test('Die Checkbox lässt nur die gemochten Songs übrig', () => {
  render(<FilterableSongs />);
  liked();
  assert.equal(titles(), 'Blue Hour, Night Bus', says('nach dem Ankreuzen von Only liked songs'));
  assert.equal(screen.getByRole('checkbox', {name: 'Only liked songs'}).checked, true, 'die Checkbox ist angekreuzt');
});

test('Suche und Checkbox wirken zusammen', () => {
  render(<FilterableSongs />);
  search('o');
  liked();
  assert.equal(titles(), 'Blue Hour', says('nach der Eingabe von "o" und dem Ankreuzen von Only liked songs'));
});

Legen Sie main.jsx in einem Vite-React-Projekt (npm create vite@latest, Vorlage react) als App.jsx in src/ ab und starten Sie es mit npm run dev. React rendert es dort wie hier in <StrictMode>.

Programm ausführen:

npm run dev

Für die Prüfungen gibt es noch keinen Befehl auf Ihrem Computer. Sie stehen in main.test.jsx, geschrieben mit node:test und React Testing Library.

Übung 2 von 2

Den redundanten State entfernen

Diese Version hält eine dritte State-Variable, visible, und aktualisiert sie in jedem Handler. Kreuzen Sie Only liked songs an und tippen Sie dann eine Suche: Die Checkbox wird vergessen. Tippen Sie eine Suche, kreuzen Sie die Box an und wieder ab: Die Suche wird vergessen. Die sichtbaren Songs lassen sich berechnen, sind also kein State. Löschen Sie den State visible samt seinen Aktualisierungen und berechnen Sie visible beim Rendern aus query und likedOnly. Behalten Sie die Eingabefelder und ihre Handler.

Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.

Beim ersten Ausführen lädt Ihr Browser React und den Compiler herunter, der JSX in JavaScript übersetzt (bis zu 2 MB), und speichert beides im Cache. Ihre Komponenten laufen in Ihrem Browser und bleiben auf Ihrem Gerät.

Ihre Seite

Ihre Seite läuft in einem abgeschotteten Rahmen. Sie hat keinen Zugriff auf diese Website, und ihr localStorage liegt nur für diesen Lauf im Speicher: Der nächste Lauf beginnt leer.

Führen Sie das Programm aus, um seine Seite hier zu sehen.

Hinweise
  1. Hinweis 1

    Welche der drei State-Variablen lässt sich aus den beiden anderen ableiten?

  2. Hinweis 2

    Die Handler sollten nur query und likedOnly setzen.

  3. Hinweis 3

    const visible = SONGS.filter(...) im Komponentenrumpf, mit query und likedOnly zusammen.

Eine Lösung zeigen

Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.

import { useState } from 'react';

const SONGS = [
  { id: 1, title: 'Blue Hour', artist: 'Mira', liked: true },
  { id: 2, title: 'Paper Boats', artist: 'Tom', liked: false },
  { id: 3, title: 'Night Bus', artist: 'Mira', liked: true },
  { id: 4, title: 'Salt', artist: 'Jun', liked: false }
];

export default function FilterableSongs() {
  const [query, setQuery] = useState('');
  const [likedOnly, setLikedOnly] = useState(false);

  const visible = SONGS.filter(
    song =>
      song.title.toLowerCase().includes(query.toLowerCase()) &&
      (!likedOnly || song.liked)
  );

  return (
    <div>
      <form>
        <input
          aria-label="Search songs"
          value={query}
          onChange={e => setQuery(e.target.value)}
        />
        <label>
          <input
            type="checkbox"
            checked={likedOnly}
            onChange={e => setLikedOnly(e.target.checked)}
          />
          Only liked songs
        </label>
      </form>
      <ul>
        {visible.map(song => <li key={song.id}>{song.title}</li>)}
      </ul>
    </div>
  );
}
Auf dem eigenen Computer ausführen

Installieren Sie React 19.3 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.

main.jsx

import { useState } from 'react';

const SONGS = [
  { id: 1, title: 'Blue Hour', artist: 'Mira', liked: true },
  { id: 2, title: 'Paper Boats', artist: 'Tom', liked: false },
  { id: 3, title: 'Night Bus', artist: 'Mira', liked: true },
  { id: 4, title: 'Salt', artist: 'Jun', liked: false }
];

export default function FilterableSongs() {
  const [query, setQuery] = useState('');
  const [likedOnly, setLikedOnly] = useState(false);
  const [visible, setVisible] = useState(SONGS);

  function handleQueryChange(value) {
    setQuery(value);
    setVisible(SONGS.filter(song => song.title.toLowerCase().includes(value.toLowerCase())));
  }

  function handleLikedOnlyChange(checked) {
    setLikedOnly(checked);
    setVisible(checked ? visible.filter(song => song.liked) : SONGS);
  }

  return (
    <div>
      <form>
        <input
          aria-label="Search songs"
          value={query}
          onChange={e => handleQueryChange(e.target.value)}
        />
        <label>
          <input
            type="checkbox"
            checked={likedOnly}
            onChange={e => handleLikedOnlyChange(e.target.checked)}
          />
          Only liked songs
        </label>
      </form>
      <ul>
        {visible.map(song => <li key={song.id}>{song.title}</li>)}
      </ul>
    </div>
  );
}

main.test.jsx

import {test} from 'node:test';
import assert from 'node:assert/strict';
import {render, screen, fireEvent} from '@testing-library/react';
import FilterableSongs from './main.jsx';

const titles = () => screen.queryAllByRole('listitem').map((li) => li.textContent).join(', ');
const search = (value) => fireEvent.change(screen.getByRole('textbox', {name: 'Search songs'}), {target: {value}});
const liked = () => fireEvent.click(screen.getByRole('checkbox', {name: 'Only liked songs'}));
const says = (what) => 'die Liste zeigt ' + JSON.stringify(titles()) + ' ' + what;

test('Die Suche filtert nach Titel', () => {
  render(<FilterableSongs />);
  search('o');
  assert.equal(titles(), 'Blue Hour, Paper Boats', says('nach der Eingabe von "o"'));
});

test('Erst ankreuzen, dann suchen: beide Filter bleiben', () => {
  render(<FilterableSongs />);
  liked();
  search('o');
  assert.equal(titles(), 'Blue Hour', says('nach dem Ankreuzen von Only liked songs und der Eingabe von "o"'));
});

test('Das Abwählen der Checkbox behält den Suchtext', () => {
  render(<FilterableSongs />);
  search('o');
  liked();
  liked();
  assert.equal(titles(), 'Blue Hour, Paper Boats', says('nach der Eingabe von "o" und dem Ankreuzen und Abwählen von Only liked songs'));
});

test('Das Leeren des Suchfelds behält die Checkbox', () => {
  render(<FilterableSongs />);
  liked();
  search('o');
  search('');
  assert.equal(titles(), 'Blue Hour, Night Bus', says('nach dem Ankreuzen, der Eingabe von "o" und dem Leeren'));
});

Legen Sie main.jsx in einem Vite-React-Projekt (npm create vite@latest, Vorlage react) als App.jsx in src/ ab und starten Sie es mit npm run dev. React rendert es dort wie hier in <StrictMode>.

Programm ausführen:

npm run dev

Für die Prüfungen gibt es noch keinen Befehl auf Ihrem Computer. Sie stehen in main.test.jsx, geschrieben mit node:test und React Testing Library.

Häufige Fehler

Ein kontrolliertes Eingabefeld, das nichts ändern kann

import { useState } from 'react';

export default function Playlist() {
  const [query, setQuery] = useState('');
  return <input aria-label="Search songs" value={query} />;
}

Was React ausgibt

You provided a `value` prop to a form field without an `onChange` handler.

Warum, und die Lösung

Das Eingabefeld hängt an query, und nichts im Programm kann query ändern, also rendert React ein schreibgeschütztes Feld. Ergänzen Sie den letzten Schritt: Setter nach unten reichen und aufrufen, <input value={query} onChange={e => setQuery(e.target.value)} />. Liegt das Eingabefeld in einem Kind, reicht der Elternteil setQuery als Prop nach unten.

Eine Prop unter anderem Namen übergeben

const SONGS = [
  { id: 1, title: 'Blue Hour', artist: 'Mira', liked: true },
  { id: 2, title: 'Paper Boats', artist: 'Tom', liked: false },
  { id: 3, title: 'Night Bus', artist: 'Mira', liked: true },
  { id: 4, title: 'Salt', artist: 'Jun', liked: false }
];

export default function Playlist() {
  return <SongList items={SONGS} />;
}

function SongList({ songs }) {
  return (
    <ul>
      {songs.map(song => <li key={song.id}>{song.title}</li>)}
    </ul>
  );
}

Was React ausgibt

TypeError: Cannot read properties of undefined (reading 'map')

Warum, und die Lösung

Playlist übergibt items, aber SongList liest songs, und eine nicht übergebene Prop ist undefined (Wortlaut von Chrome und Node; Firefox und Safari formulieren anders). Geben Sie beim Bau der statischen Version Elternteil und Kind denselben Prop-Namen: <SongList songs={SONGS} />.

Eine statische Liste ohne keys

const SONGS = [
  { id: 1, title: 'Blue Hour', artist: 'Mira', liked: true },
  { id: 2, title: 'Paper Boats', artist: 'Tom', liked: false },
  { id: 3, title: 'Night Bus', artist: 'Mira', liked: true },
  { id: 4, title: 'Salt', artist: 'Jun', liked: false }
];

export default function Playlist() {
  return <SongList songs={SONGS} />;
}

function SongList({ songs }) {
  return (
    <ul>
      {songs.map(song => <li>{song.title}</li>)}
    </ul>
  );
}

Was React ausgibt

Each child in a list should have a unique "key" prop.

Warum, und die Lösung

Zur statischen Version gehören auch die Listen. Jeder Eintrag aus einem Array braucht einen key, der stabil und unter seinen Geschwistern eindeutig ist, etwa die id aus dem Datenmodell: <li key={song.id}>.

React im Browser: React 19.3.0 (Entwicklungs-Build) und React Testing Library, MIT, von dieser Website ausgeliefert; JSX übersetzt TypeScript 6.0.3, Apache-2.0. Lizenz und Quellcode

Abschlussquiz

5 Fragen, ohne Hinweise. Ab 80 % ist die Lektion abgeschlossen.

Erledigen Sie zuerst alle Aufgaben oben, um das Abschlussquiz freizuschalten.

Problem melden

Etwas ist falsch oder unklar? Beschreiben Sie es kurz, dann wird es geprüft und korrigiert.

#

Mindestens 20 Zeichen.

Nur, wenn Sie eine Antwort wünschen.

Kernideen

Fünf Schritte, der Reihe nach

Beginnen Sie mit einem Entwurf. Schritt 1: Zeichnen Sie um jede Komponente einen Kasten und benennen Sie sie; jede Komponente sollte zu einem Teil Ihres Datenmodells passen. Schritt 2: Bauen Sie eine statische Version, die Daten nur per Props nach unten reicht, ganz ohne State. Schritt 3: Finden Sie den minimalen State. Schritt 4: Entscheiden Sie, welche Komponente ihn besitzt. Schritt 5: Fügen Sie den umgekehrten Datenfluss hinzu, damit Eingaben diesen State ändern können. Der Reihe nach bleibt jede Frage klein. Daten, die von der obersten Komponente per Props nach unten fließen, heißen One-Way Data Flow (Datenfluss in eine Richtung).

State ist das Minimum, der Rest wird berechnet

Halten Sie State so klein wie möglich und berechnen Sie alles andere bei Bedarf. Stellen Sie jedem Datum drei Fragen. Bleibt es über die Zeit unverändert? Dann ist es kein State. Kommt es per Props von einem Elternteil? Dann ist es kein State. Lässt es sich aus vorhandenem State oder Props berechnen? Dann ist es ganz sicher kein State. In einer filterbaren Liste kommen die Songs als Props; der Suchtext und die Checkbox sind State; die gefilterte Liste wird beim Rendern berechnet, nicht gespeichert. Eine gespeicherte Kopie müssten Sie von Hand synchron halten.

Der Besitzer ist der nächste gemeinsame Elternteil

Suchen Sie für jeden State alle Komponenten, die etwas daraus rendern, und dann deren nächsten gemeinsamen Elternteil. Oft lebt der State genau dort. Hier brauchen SearchBar und SongList den Suchtext und die Checkbox, also besitzt FilterablePlaylist sie. Besitzen reicht aber nicht: Ein Kind kann eine Prop nicht ändern, also reicht der Elternteil die Setter als Props nach unten, etwa onQueryChange={setQuery}, und das Kind ruft sie in seinen onChange-Handlern auf. Daten fließen in Props nach unten, Änderungen fließen durch Aufrufe nach oben: umgekehrter Datenfluss.

Quellen

Zuletzt geprüft am 5. Oktober 2026