Seien Sie vorsichtig, wenn Sie über Arrays iterieren !!
Es ist ein weit verbreitetes Missverständnis, dass die Verwendung des Index des Elements im Array eine akzeptable Methode ist, um den Fehler zu unterdrücken, mit dem Sie wahrscheinlich vertraut sind:
Each child in an array should have a unique "key" prop.
In vielen Fällen ist dies jedoch nicht der Fall! Dies ist ein Anti-Muster , das in einigen Situationen zu unerwünschtem Verhalten führen kann .
Die key
Requisite verstehen
React verwendet die key
Requisite, um die Beziehung zwischen Komponente und DOM-Element zu verstehen, die dann für den Abstimmungsprozess verwendet wird . Es ist daher sehr wichtig, dass der Schlüssel immer eindeutig bleibt , da sonst die Wahrscheinlichkeit groß ist, dass React die Elemente verwechselt und das falsche mutiert. Es ist auch wichtig, dass diese Schlüssel bei allen erneuten Rendern statisch bleiben , um die beste Leistung zu erzielen.
Davon abgesehen muss man das oben Gesagte nicht immer anwenden, vorausgesetzt, es ist bekannt, dass das Array vollständig statisch ist. Die Anwendung von Best Practices wird jedoch nach Möglichkeit empfohlen.
Ein React-Entwickler sagte in dieser GitHub-Ausgabe :
- Bei key geht es nicht wirklich um Leistung, sondern eher um Identität (was wiederum zu einer besseren Leistung führt). zufällig zugewiesene und sich ändernde Werte sind keine Identität
- Wir können Schlüssel nicht [automatisch] realistisch bereitstellen, ohne zu wissen, wie Ihre Daten modelliert werden. Ich würde vorschlagen, vielleicht eine Art Hashing-Funktion zu verwenden, wenn Sie keine IDs haben
- Wir haben bereits interne Schlüssel, wenn wir Arrays verwenden, aber sie sind der Index im Array. Wenn Sie ein neues Element einfügen, sind diese Schlüssel falsch.
Kurz gesagt, a key
sollte sein:
- Einzigartig - Ein Schlüssel kann nicht mit dem einer Geschwisterkomponente identisch sein .
- Statisch - Ein Schlüssel sollte niemals zwischen Renderings wechseln.
Mit der key
Stütze
Studieren Sie gemäß der obigen Erläuterung die folgenden Beispiele sorgfältig und versuchen Sie, den empfohlenen Ansatz nach Möglichkeit umzusetzen.
Schlecht (möglicherweise)
<tbody>
{rows.map((row, i) => {
return <ObjectRow key={i} />;
})}
</tbody>
Dies ist wohl der häufigste Fehler, der beim Durchlaufen eines Arrays in React auftritt. Dieser Ansatz ist technisch nicht "falsch" , sondern nur ... "gefährlich", wenn Sie nicht wissen, was Sie tun. Wenn Sie ein statisches Array durchlaufen, ist dies ein absolut gültiger Ansatz (z. B. ein Array von Links in Ihrem Navigationsmenü). Wenn Sie jedoch Elemente hinzufügen, entfernen, neu anordnen oder filtern, müssen Sie vorsichtig sein. Schauen Sie sich diese ausführliche Erklärung in der offiziellen Dokumentation an.
class MyApp extends React.Component {
constructor() {
super();
this.state = {
arr: ["Item 1"]
}
}
click = () => {
this.setState({
arr: ['Item ' + (this.state.arr.length+1)].concat(this.state.arr),
});
}
render() {
return(
<div>
<button onClick={this.click}>Add</button>
<ul>
{this.state.arr.map(
(item, i) => <Item key={i} text={"Item " + i}>{item + " "}</Item>
)}
</ul>
</div>
);
}
}
const Item = (props) => {
return (
<li>
<label>{props.children}</label>
<input value={props.text} />
</li>
);
}
ReactDOM.render(<MyApp />, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
In diesem Snippet verwenden wir ein nicht statisches Array und beschränken uns nicht darauf, es als Stapel zu verwenden. Dies ist ein unsicherer Ansatz (Sie werden sehen warum). Beachten Sie, dass beim Hinzufügen von Elementen am Anfang des Arrays (im Grunde genommen die Verschiebung) der Wert für jedes Element erhalten <input>
bleibt. Warum? Weil das key
nicht jedes Element eindeutig identifiziert.
Mit anderen Worten, zunächst Item 1
hat key={0}
. Wenn wir das zweite Element hinzufügen, wird das oberste Element Item 2
gefolgt von Item 1
dem zweiten Element. Doch jetzt Item 1
hat key={1}
und nicht key={0}
mehr. Stattdessen hat Item 2
jetzt key={0}
!!
Aus diesem <input>
Grund glaubt React, dass sich die Elemente nicht geändert haben, da der Item
With-Schlüssel 0
immer oben steht!
Warum ist dieser Ansatz nur manchmal schlecht?
Dieser Ansatz ist nur dann riskant, wenn das Array irgendwie gefiltert, neu angeordnet oder Elemente hinzugefügt / entfernt werden. Wenn es immer statisch ist, ist es absolut sicher zu bedienen. Zum Beispiel kann ein Navigationsmenü wie ["Home", "Products", "Contact us"]
mit dieser Methode sicher durchlaufen werden, da Sie wahrscheinlich nie neue Links hinzufügen oder neu anordnen werden.
Kurz gesagt, hier ist, wann Sie den Index sicher verwenden können als key
:
- Das Array ist statisch und wird sich nie ändern.
- Das Array wird niemals gefiltert (eine Teilmenge des Arrays anzeigen).
- Das Array wird nie neu angeordnet.
- Das Array wird als Stack oder LIFO verwendet (last in, first out). Mit anderen Worten, das Hinzufügen kann nur am Ende des Arrays erfolgen (dh Push), und nur das letzte Element kann jemals entfernt werden (dh Pop).
Hätten wir stattdessen in dem Snippet oben, schoben das zusätzliche Element am Ende des Arrays, die Reihenfolge für jedes vorhandenes Element würde immer richtig sein.
Sehr schlecht
<tbody>
{rows.map((row) => {
return <ObjectRow key={Math.random()} />;
})}
</tbody>
Während dieser Ansatz wahrscheinlich die Eindeutigkeit der Schlüssel garantiert, erzwingt er immer eine Reaktion, um jedes Element in der Liste neu zu rendern, selbst wenn dies nicht erforderlich ist. Dies ist eine sehr schlechte Lösung, da sie die Leistung stark beeinträchtigt. Ganz zu schweigen davon, dass man die Möglichkeit einer Schlüsselkollision nicht ausschließen kann, wenn Math.random()
dieselbe Zahl zweimal erzeugt wird.
Instabile Schlüssel (wie die von Math.random()
) führen dazu, dass viele Komponenteninstanzen und DOM-Knoten unnötig neu erstellt werden, was zu Leistungseinbußen und Statusverlust bei untergeordneten Komponenten führen kann.
Sehr gut
<tbody>
{rows.map((row) => {
return <ObjectRow key={row.uniqueId} />;
})}
</tbody>
Dies ist wohl der beste Ansatz, da eine Eigenschaft verwendet wird, die für jedes Element im Dataset eindeutig ist. Wenn beispielsweise rows
Daten enthalten sind, die aus einer Datenbank abgerufen wurden, kann der Primärschlüssel der Tabelle verwendet werden ( normalerweise eine automatisch inkrementierende Zahl ).
Der beste Weg, einen Schlüssel auszuwählen, besteht darin, eine Zeichenfolge zu verwenden, die ein Listenelement unter seinen Geschwistern eindeutig identifiziert. Am häufigsten verwenden Sie IDs aus Ihren Daten als Schlüssel
Gut
componentWillMount() {
let rows = this.props.rows.map(item => {
return {uid: SomeLibrary.generateUniqueID(), value: item};
});
}
...
<tbody>
{rows.map((row) => {
return <ObjectRow key={row.uid} />;
})}
</tbody>
Dies ist auch ein guter Ansatz. Wenn Ihr Datensatz keine Daten enthält, die die Eindeutigkeit garantieren ( z. B. ein Array beliebiger Zahlen ), besteht die Möglichkeit einer Schlüsselkollision. In solchen Fällen ist es am besten, manuell eine eindeutige Kennung für jedes Element im Dataset zu generieren, bevor Sie darüber iterieren. Vorzugsweise beim Mounten der Komponente oder beim Empfangen des Datasets ( z. B. von props
oder von einem asynchronen API-Aufruf ), um dies nur einmal und nicht jedes Mal zu tun , wenn die Komponente erneut gerendert wird. Es gibt bereits eine Handvoll Bibliotheken, die Ihnen solche Schlüssel zur Verfügung stellen können. Hier ist ein Beispiel: React-Key-Index .
key
Eigenschaft haben. Es wird ReactJS helfen, Verweise auf die entsprechenden DOM-Knoten zu finden und nur den Inhalt innerhalb des Markups zu aktualisieren, aber nicht die gesamte Tabelle / Zeile neu zu rendern.