Menu

React Listen und Keys: map(), key-Prop, Index als Key

Um in React eine Liste zu rendern, rufst du map() auf einem Array auf und gibst für jeden Eintrag JSX zurück, mit einer key-Prop an jedem Eintrag. Lerne filter vor map, woher Keys kommen sollten und warum Array-Indizes als Keys Einträge vertauschen, wenn sich die Liste ändert.

Diese Seite enthält ausführbare Editoren - bearbeiten, ausführen und Ausgabe sofort sehen.

Um in React eine Liste zu rendern, rufst du map() auf einem Array auf und gibst für jeden Eintrag ein Stück JSX zurück. Gib dem äußersten Element jedes Eintrags eine key-Prop mit einem Wert, der diesen Eintrag identifiziert, meist seine ID, damit React die Einträge unterscheiden kann, wenn sich die Liste ändert.

Füge { id: 'dat', name: 'Date', price: 4 } zum Array hinzu, und eine vierte Zeile erscheint. JSX hat keine eigene Schleifensyntax: map gibt ein Array von Elementen zurück, und React rendert ein Array, indem es jedes Element der Reihe nach rendert.

Arrays mit map rendern

map ruft deine Funktion einmal pro Eintrag auf und sammelt die Ergebnisse in einem neuen Array. In den geschweiften Klammern von JSX ist dieses Array einfach ein Wert, also kannst du direkt im Markup mappen, wie oben, oder das Array vorher bauen:

const items = fruits.map((fruit) => <li key={fruit.id}>{fruit.name}</li>);
return <ul>{items}</ul>;

Zwei Details bringen Leute ins Stolpern. Wenn du in der Arrow Function geschweifte Klammern nutzt, brauchst du ein return: fruits.map((f) => { return <li key={f.id}>{f.name}</li>; }). Ohne gibt die Funktion undefined zurück, und die Liste ist leer. Und der key gehört an das Element, das aus map zurückkommt, nicht an ein Element darin. Wenn der Eintrag eine Komponente ist, setze den Key auf die Komponente: <FruitRow key={fruit.id} fruit={fruit} />.

Erst filtern, dann mappen

Um einige der Einträge zu zeigen, filtere das Array zuerst und mappe dann das Ergebnis. Jede Methode erledigt eine Aufgabe, und die Kette liest sich wie der Satz „die passenden Früchte, als Listeneinträge“.

Tippe er ins Suchfeld, und die Liste schrumpft auf Eraser, Ruler und Stapler; hake In stock only an, und Ruler verschwindet auch. Die gefilterte Liste wird beim Rendern aus den zwei State-Werten berechnet, also gibt es kein zweites Array im State, das synchron gehalten werden muss. visible.length === 0 && ist eine sichere Verwendung von &&, weil der Vergleich ein echter Boolean ist (die Seite zum bedingten Rendern zeigt, was mit einer nackten 0 passiert).

Was Keys tun

Wenn eine Liste erneut rendert, vergleicht React das neue Array von Elementen mit dem vorherigen. Keys sind die Art, wie es sie zuordnet: Das Element mit Key 3 jetzt ist das Element mit Key 3 von vorher, auch wenn es sich bewegt hat. React behält den DOM-Knoten und den Komponenten-State dieses Eintrags und verschiebt oder aktualisiert nur, was sich geändert hat. Einträge mit neuen Keys werden erzeugt, und verschwundene Keys werden entfernt.

Ohne Keys kann React nur nach Position zuordnen. Das passt, solange sich nichts bewegt, und ist falsch, sobald sich etwas bewegt.

Warum Index-Keys brechen

map übergibt den Index als zweites Argument, also ist key={index} verlockend. Der Index beschreibt eine Position, keinen Eintrag. Füge oben einen Eintrag ein, und der Index jedes Eintrags verschiebt sich um eins, also glaubt React, Eintrag 0 sei immer noch Eintrag 0, und behält dort seinen alten State.

Die Demo unten rendert dieselbe Liste zweimal, einmal mit Index als Key und einmal mit ID. Jede Zeile hat ein Eingabefeld, das seinen eigenen Text behält.

Tippe in beiden Listen math neben Ada und klicke dann auf Add to top. In der ID-Liste bleibt die Notiz bei Ada. In der Index-Liste springt die Notiz zur neuen Person, weil das erste Eingabefeld immer noch das Eingabefeld an Position 0 ist, und Position 0 ist jetzt jemand anderes. Die Namen stimmen in beiden Listen, weil sie aus den Daten kommen; nur der State, der im DOM und in Komponenten lebt, geht schief. Echte Apps stoßen darauf bei Checkboxen, Fokus, Animationen und jeder Komponente, die useState hält.

Index-Keys sind nur sicher, wenn alles davon zutrifft: Die Liste wird nie umsortiert oder gefiltert, Einträge werden nie eingefügt oder entfernt außer am Ende, und die Einträge haben keinen eigenen State. Eine statische Liste von Footer-Links erfüllt das. Alles, was jemand bearbeiten kann, meist nicht.

Woher Keys kommen

Der beste Key ist eine ID, die schon in den Daten steckt: eine Datenbank-ID, eine Artikelnummer, ein Slug, ein Benutzername. Daten von einem Server haben fast immer eine.

Für Einträge, die im Browser entstehen, gib jedem Eintrag beim Anlegen eine ID und speichere sie mit dem Eintrag: einen Zähler, wie in der Demo oben, oder crypto.randomUUID(). Erzeuge den Key niemals beim Rendern:

// Wrong: a new key on every render, so React remounts every item every time
{todos.map((todo) => <Todo key={Math.random()} todo={todo} />)}

// Right: the ID is created once, in the event that adds the item
function addTodo(text) {
    setTodos([...todos, { id: crypto.randomUUID(), text }]);
}

Ein Key, der sich bei jedem Render ändert, ist schlimmer als ein Index: Jeder Eintrag wird jedes Mal zerstört und neu erzeugt, Eingabefelder verlieren beim Tippen den Fokus, und nichts behält seinen State. Derselbe Trick ist absichtlich nützlich: Den Key einer Komponente zu ändern setzt sie zurück, und die Seite zu useState nutzt das, um ein Formular zu leeren.

Keys sind nur unter Geschwistern eindeutig

Ein Key muss innerhalb einer Liste eindeutig sein, nicht in der ganzen App. Zwei getrennte Listen können dieselben IDs wiederverwenden, weil React Keys nur unter den Kindern desselben Elternelements vergleicht.

Beide Abschnitte haben Gerichte mit den Keys 1 und 2, und das ist korrekt: Jedes ul ist eine eigene Liste. Doppelte Keys innerhalb einer Liste sind das Problem: React kann die kollidierenden Einträge nicht unterscheiden und kann sie beim Update der Liste doppeln oder verlieren.

Wenn ein Eintrag mehrere Geschwisterelemente rendert, etwa ein <dt> und ein <dd>, umschließe sie mit <Fragment key={item.id}>. Die kurze Syntax <> kann keinen Key annehmen.

Die Warnung „unique key prop“

Wenn du den Key weglässt, gibt React in der Entwicklung dies in der Browserkonsole aus:

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

Check the render method of `App`.

Die zweite Zeile nennt die Komponente, deren Render die Liste erzeugt hat, und sagt dir so, welches map du korrigieren musst. Zwei Geschwister mit demselben Key bekommen eine andere Meldung, die mit „Encountered two children with the same key“ beginnt. Die Vorschau auf dieser Seite läuft wie ein Produktions-Build, der diese Prüfung überspringt, also siehst du die Warnung hier nicht, selbst wenn du einen key löschst. Lösch trotzdem einen im ersten Beispiel: Die Liste rendert weiter, weil ein fehlender Key ein Risiko für die Korrektheit ist, kein Absturz. Um die Warnung zu beheben, setze einen stabilen Key auf das aus map zurückgegebene Element; key={index} bringt sie zum Schweigen, holt aber den oben gezeigten Bug zurück.

Beachte außerdem, dass key nicht an deine Komponente übergeben wird. In FruitRow ist props.key gleich undefined. Wenn die Komponente die ID braucht, übergib sie ein zweites Mal als normale Prop: <FruitRow key={f.id} id={f.id} />.

Häufig gestellte Fragen

Warum braucht React einen key an Listeneinträgen?

Keys sagen React, welcher Eintrag welcher ist, über Renders hinweg. Wenn sich das Array ändert, ordnet React alte und neue Einträge per Key zu, damit es State und DOM-Knoten jedes Eintrags bei den richtigen Daten behält, statt nach Position zu raten.

Kann ich den Array-Index als Key verwenden?

Nur für eine Liste, die nie ihre Reihenfolge ändert und in deren Mitte nie Einträge eingefügt oder entfernt werden. Wenn sich Einträge bewegen können, bindet ein Index-Key den State an die Position, also landen getippter Text, Checkboxen und Fokus beim falschen Eintrag.

Was sollte ich als Key verwenden?

Eine ID, die zu den Daten gehört: eine Datenbank-ID, eine Artikelnummer, ein Benutzername. Für Einträge, die im Browser entstehen, erzeuge die ID einmal beim Anlegen des Eintrags (ein Zähler oder crypto.randomUUID()), niemals beim Rendern.

Müssen Keys in der ganzen App eindeutig sein?

Nein. Keys müssen nur unter Geschwistern in derselben Liste eindeutig sein. Zwei verschiedene Listen können dieselben Keys verwenden.

Kann eine Komponente ihren eigenen Key lesen?

Nein. key wird von React genutzt und nicht als Prop an die Komponente übergeben. Wenn die Komponente die ID braucht, übergib sie erneut unter einem anderen Namen, etwa id={item.id}.

Wie behebe ich „Each child in a list should have a unique key prop“?

Setze einen key auf das äußerste Element, das du aus map zurückgibst, mit einer stabilen ID aus dem Eintrag. Wenn dieses Element ein Fragment ist, nutze <Fragment key={id}>, weil <> keinen Key annehmen kann.

Illustration der Programmiersprachen bei Coddy

Lerne mit Coddy zu programmieren

LOS GEHT'S