Sagen Sie voraus, bevor Sie weiterlesen. Sie klicken den Stern neben Ada an, dann Add Linus, das Linus oben einfügt. Welcher Name hat jetzt den gefüllten Stern ★ (nobody = niemand)?
Knobelaufgabe. Der Key ist da, aber am <b> in jedem <li>. Was gibt React aus (Antworten auf Englisch: no warning = keine Warnung, a key warning = eine Key-Warnung)?
Mini-Aufgabe: eine Leseliste. Legen Sie in main.jsx ein Array mit drei Büchern an, jedes mit id, title, author und einem read-Flag. Zeigen Sie nur die noch nicht gelesenen Bücher: filtern, dann jedes Buch auf ein <dt> mit dem Titel und ein <dd> mit dem Autor in einem <dl> mappen. Jedes Buch rendert zwei Elemente, also gruppieren Sie sie in einem Fragment mit der id des Buchs als Key (import { Fragment } from 'react'). Führen Sie es aus: Es darf keine Warnung erscheinen.
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 Ladenliste aus Daten
Die Produkte stehen in data.js, jedes mit einer id, wie react.dev es empfiehlt. Shop filtert das Obst und mappt es auf <li>-Elemente mit key={product.id}. Die Preisliste mappt jedes Produkt auf zwei Elemente, ein <dt> und ein <dd>, gruppiert in einem Fragment, das den Key trägt. Dieser Callback hat einen Block-Body, weil er zuerst den Preis ausrechnet, und braucht deshalb sein eigenes return.
filter() behielt Apple und Banana in ihrer ursprünglichen Reihenfolge, und map() machte je ein <li>.
Die Fragments hinterließen keine Spur: Das <dl> enthält eine flache Folge von <dt> und <dd>.
Im HTML erscheint kein Key; React behält Keys für sich.
Vor dem Snapshot wurde nichts ausgegeben, React gab also keine Key-Warnung.
Ä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
Nur die Engineers
Die Teamseite listet alle auf, und React warnt vor fehlenden Keys. Ändern Sie sie so, dass sie nur die Personen mit der Rolle Engineer zeigt, jede als <li> wie "Mira Okafor: Engineer", in der Reihenfolge aus data.js. Filtern Sie zuerst, mappen Sie dann, und geben Sie jedem <li> einen Key aus den Daten.
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
Hinweis 1
people.filter(person => person.role === 'Engineer') gibt ein neues Array mit den drei Engineers.
Hinweis 2
Mappen Sie dieses Array statt people.
Hinweis 3
Jede Person hat eine id, also bekommt das <li> key={person.id}.
Eine Lösung zeigen
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
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 { people } from './data.js';
// TODO: list only the engineers, each as <li>Name: role</li>,
// with a key that comes from the data.
export default function Engineers() {
const listItems = people.map(person => <li>{person.name}: {person.role}</li>);
return (
<section>
<h1>Engineers</h1>
<ul>{listItems}</ul>
</section>
);
}
main.test.jsx
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {render, screen} from '@testing-library/react';
import Engineers from './main.jsx';
test('React gibt keine Warnungen aus', () => {
const warnings = [];
const original = console.error;
console.error = (...args) => warnings.push(args.join(' '));
try {
render(<Engineers />);
} finally {
console.error = original;
}
assert.deepEqual(warnings, [], 'console.error beim Rendern');
});
test('Nur die drei Engineers stehen in der Liste, in ihrer Reihenfolge', () => {
render(<Engineers />);
const items = screen.getAllByRole('listitem').map((li) => li.textContent);
assert.deepEqual(items, ['Mira Okafor: Engineer', 'Lena Wolf: Engineer', 'Ines Duarte: Engineer'], 'die Liste lautet ' + JSON.stringify(items));
});
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
Sterne, die bleiben
Geben Sie Ada einen Stern und fügen Sie dann oben einen Gast hinzu: Der Stern springt zum neuen Gast, weil die Liste den Index als Key nimmt. Beheben Sie das: Geben Sie jedem neuen Gast in handleAdd, wo er angelegt wird, eine id (der Zähler nextNumber ist dafür da), und nehmen Sie die id als Key. Behalten Sie die Button-Texte; die Tests klicken sie.
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
Hinweis 1
Der Stern-State gehört zu einem Guest, und React findet diesen Guest über seinen Key wieder. Index 0 ist nach einem Einfügen oben ein anderer Gast.
Hinweis 2
Ergänzen Sie in handleAdd eine id am neuen Objekt: { id: 'new' + nextNumber, name: 'Guest ' + nextNumber }.
Hinweis 3
Lassen Sie im map den index weg und schreiben Sie <Guest key={guest.id} name={guest.name} />.
Eine Lösung zeigen
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
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';
function Guest({ name }) {
const [starred, setStarred] = useState(false);
return (
<li>
{name}{' '}
<button aria-pressed={starred} onClick={() => setStarred(!starred)}>
Star {name}
</button>
</li>
);
}
let nextNumber = 1;
export default function GuestList() {
const [guests, setGuests] = useState([
{ id: 'g1', name: 'Ada' },
{ id: 'g2', name: 'Grace' }
]);
function handleAdd() {
// TODO: give the new guest an id here, where it is created.
const guest = { name: 'Guest ' + nextNumber };
nextNumber++;
setGuests([guest, ...guests]);
}
return (
<>
<button onClick={handleAdd}>Add a guest at the top</button>
<ul>
{guests.map((guest, index) => (
<Guest key={index} name={guest.name} />
))}
</ul>
</>
);
}
main.test.jsx
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {render, screen} from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import GuestList from './main.jsx';
test('React gibt keine Warnungen aus, während zwei Gäste hinzukommen', async () => {
const user = userEvent.setup();
const warnings = [];
const original = console.error;
console.error = (...args) => warnings.push(args.join(' '));
try {
render(<GuestList />);
await user.click(screen.getByRole('button', {name: 'Add a guest at the top'}));
await user.click(screen.getByRole('button', {name: 'Add a guest at the top'}));
} finally {
console.error = original;
}
assert.deepEqual(warnings, [], 'console.error beim Hinzufügen der Gäste');
});
test('Ein neuer Gast erscheint oben', async () => {
const user = userEvent.setup();
render(<GuestList />);
await user.click(screen.getByRole('button', {name: 'Add a guest at the top'}));
const first = screen.getAllByRole('listitem')[0].textContent;
assert.match(first, /^Guest/, 'das erste Item lautet ' + JSON.stringify(first));
});
test('Ada behält ihren Stern, wenn oben ein Gast hinzukommt', async () => {
const user = userEvent.setup();
render(<GuestList />);
await user.click(screen.getByRole('button', {name: 'Star Ada'}));
await user.click(screen.getByRole('button', {name: 'Add a guest at the top'}));
const ada = screen.getByRole('button', {name: 'Star Ada'}).getAttribute('aria-pressed');
assert.equal(ada, 'true', 'Ada hat keinen Stern mehr');
const starred = screen.getAllByRole('button', {pressed: true}).map((button) => button.textContent);
assert.deepEqual(starred, ['Star Ada'], 'Buttons mit Stern: ' + JSON.stringify(starred));
});
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.
Der Code stimmt, die Daten nicht: Cherry wurde von Banana kopiert und hat dessen id behalten. Keys müssen unter Geschwistern eindeutig sein, also warnt React, und bei Duplikaten kann es Items doppelt zeigen oder auslassen, wenn sich die Liste ändert. Korrigieren Sie die Daten (Cherry bekommt f3). Wenn Sie Items selbst anlegen, macht ein Zähler oder crypto.randomUUID() die id eindeutig.
`key` is not a prop. Trying to access it will result in `undefined` being returned.
Warum, und die Lösung
React behält den Key für sich und reicht ihn nicht weiter, also ist key in Fruit undefined, und die Items lauten ": Apple". Die Warnung beginnt mit dem Komponentennamen, Fruit:. Braucht die Komponente die id, übergeben Sie sie als zweites Prop: <Fruit key={fruit.id} id={fruit.id} name={fruit.name} />.
Anders als die Kurzform <> ist <Fragment> eine Komponente, die React exportiert, also muss die Datei sie importieren. Der Lauf gibt Uncaught ReferenceError aus (Formulierung von Chrome, Firefox und Node.js; Safari formuliert es anders). Ergänzen Sie oben in main.jsx import { Fragment } from 'react';.
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.
Kernideen
map() macht aus Daten JSX, filter() wählt vorher aus
Halten Sie die Daten in einem Array und lassen Sie JavaScript die Liste bauen: people.map(person => <li>{person.name}</li>) gibt ein <li> pro Item zurück, und {listItems} in einem <ul> zeigt sie alle. Um nur einige Items zu zeigen, filtern Sie zuerst: people.filter(person => person.role === 'Engineer') gibt ein neues, kürzeres Array zurück, das Sie dann mappen. Eine Pfeilfunktion mit einem Ausdruck nach => gibt ihn zurück. Mit einer geschweiften Klammer nach => hat sie einen Block-Body, und Sie müssen return selbst schreiben; react.dev warnt: Vergessen Sie es, wird nichts zurückgegeben.
Ein Key sagt React, welches Item welches ist
Jedes Element direkt in einem map()-Aufruf braucht einen Key: <li key={person.id}>. Der Key gehört an das äußerste Element, das der Callback zurückgibt, nicht an etwas darin. Keys lassen React Items zwischen Renderings zuordnen, wenn sie sich verschieben, eingefügt oder gelöscht werden. Nehmen Sie sie aus den Daten: IDs aus einer Datenbank, oder bei selbst erzeugten Daten einen Zähler oder crypto.randomUUID() in dem Moment, in dem Sie das Item anlegen, nie beim Rendern. Keys müssen unter Geschwistern eindeutig sein und dürfen sich nicht ändern. Braucht ein Item mehrere Elemente, packen Sie sie in <Fragment key={…}>; die Kurzform <> nimmt keinen Key.
Nicht der Index, kein Zufall, kein Prop
Ohne Key nimmt React die Position. key={index} tut dasselbe, und das geht schief, sobald Items eingefügt, gelöscht oder umsortiert werden: State wie ein Stern oder ein getippter Wert bleibt an seiner Position und landet neben einem anderen Item. key={Math.random()} ist schlimmer: Die Keys passen nie zusammen, also baut React bei jedem Rendern jedes Item neu, und Eingaben gehen verloren. Der Key ist nur ein Hinweis für React: Ihre Komponente bekommt ihn nicht, props.key ist undefined, und React warnt. Braucht die Komponente die ID, übergeben Sie sie zusätzlich als anderes Prop: <Profile key={id} userId={id} />.