wake-up-neo.net

Grundlegendes zu eindeutigen Schlüsseln für Array-Kinder in React.js

Ich baue eine React-Komponente, die eine JSON-Datenquelle akzeptiert und eine sortierbare Tabelle erstellt.
Jeder der dynamischen Datenzeilen ist ein eindeutiger Schlüssel zugewiesen. Ich erhalte jedoch immer noch einen Fehler: 

Jedes Kind in einem Array sollte eine eindeutige "Schlüssel" -Stütze haben.
Überprüfen Sie die Render-Methode von TableComponent.

Meine TableComponent Render-Methode gibt Folgendes zurück:

<table>
  <thead key="thead">
    <TableHeader columns={columnNames}/>
  </thead>
  <tbody key="tbody">
    { rows }
  </tbody>
</table>

Die TableHeader-Komponente ist eine einzelne Zeile und es ist auch ein eindeutiger Schlüssel zugewiesen.

Jeder row in rows besteht aus einer Komponente mit einem eindeutigen Schlüssel:

<TableRowItem key={item.id} data={item} columns={columnNames}/>

Und der TableRowItem sieht so aus:

var TableRowItem = React.createClass({
  render: function() {

    var td = function() {
        return this.props.columns.map(function(c) {
          return <td key={this.props.data[c]}>{this.props.data[c]}</td>;
        }, this);
      }.bind(this);

    return (
      <tr>{ td(this.props.item) }</tr>
    )
  }
});

Was verursacht den eindeutigen Propellerfehler? 

404
Brett DeWoody

Sie sollten jedem Kind sowie jedem Element innerhalb von Kindern einen Schlüssel hinzufügen.

Auf diese Weise kann React die minimale DOM-Änderung verarbeiten. 

In Ihrem Code versucht jeder <TableRowItem key={item.id} data={item} columns={columnNames}/>, einige untergeordnete Elemente ohne Schlüssel darzustellen.

Überprüfen Sie dieses Beispiel .

Versuchen Sie, den key={i} aus dem <b></b>-Element in den Divs zu entfernen (und überprüfen Sie die Konsole).

Wenn wir im Beispiel dem <b>-Element keinen Schlüssel geben und wir only the object.city aktualisieren möchten, muss React die gesamte Zeile gegenüber dem Element erneut rendern. 

Hier ist der Code:

var data = [{name:'Jhon', age:28, city:'HO'},
            {name:'Onhj', age:82, city:'HN'},
            {name:'Nohj', age:41, city:'IT'}
           ];

var Hello = React.createClass({

    render: function() {

      var _data = this.props.info;
      console.log(_data);
      return(
        <div>
            {_data.map(function(object, i){
               return <div className={"row"} key={i}> 
                          {[ object.name ,
                             // remove the key
                             <b className="fosfo" key={i}> {object.city} </b> , 
                             object.age
                          ]}
                      </div>; 
             })}
        </div>
       );
    }
});

React.render(<Hello info={data} />, document.body);

Die Antwort, die von @Chris am Ende gepostet wird, geht weitaus detaillierter als diese Antwort. Bitte schauen Sie nach https://stackoverflow.com/a/43892905/2325522

Reagieren Sie Dokumentation über die Bedeutung von Schlüsseln beim Abgleich: Schlüssel

402
jmingov

Vorsicht beim Durchlaufen von Arrays !!

Es ist ein verbreiteter Irrtum, 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 Anti-Muster, das in einigen Situationen zu nerwünschtem Verhalten führen kann.


Grundlegendes zur Stütze key

React verwendet die Requisite key, um die Beziehung zwischen Komponente und DOM-Element zu verstehen, die dann für den - Abgleichsprozess verwendet wird. Es ist daher sehr wichtig, dass der Schlüssel immer bleibt eindeutig, sonst besteht eine gute Chance React wird das verwechseln Es ist auch wichtig, dass diese Schlüssel statisch bleiben bei allen Re-Renderings, um die beste Leistung zu erzielen.

Davon abgesehen muss man nicht immer das Obige 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 diesem GitHub-Problem :

  • bei key geht es nicht wirklich um Leistung, sondern vielmehr 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 Hash-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, ein key sollte sein:

  • Eindeutig - Ein Schlüssel kann nicht mit dem einer Geschwisterkomponente identisch sein.
  • Statisch - Ein Schlüssel sollte sich niemals zwischen Darstellungen ändern.


Verwenden Sie die Stütze key

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 gesehen nicht "falsch", er ist nur ... "gefährlich", wenn Sie nicht wissen, was Sie tun. Wenn Sie durch ein statisches Array iterieren, ist dies eine absolut gültige Methode (z. B. eine Reihe 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, wie beim Hinzufügen von Elementen am Anfang des Arrays (im Grunde genommen ohne Verschiebung) der Wert für jedes <input> Beibehalten wird. Warum? Weil das key nicht jedes Element eindeutig identifiziert.

Mit anderen Worten, zuerst hat Item 1key={0}. Wenn wir das zweite Element hinzufügen, wird das oberste Element zu Item 2, Gefolgt von Item 1 Als zweitem Element. Jetzt hat Item 1 Jedoch key={1} Und nicht mehr key={0}. Stattdessen hat Item 2 Jetzt key={0} !!

React denkt, die <input> - Elemente haben sich nicht geändert, da das Item mit der Taste 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. Beispielsweise kann ein Navigationsmenü wie ["Home", "Products", "Contact us"] Mit dieser Methode problemlos durchlaufen werden, da Sie wahrscheinlich niemals neue Links hinzufügen oder neu anordnen werden.

Kurz gesagt, hier ist, wann Sie sicher den Index als key verwenden können:

  • Das Array ist statisch und wird sich nie ändern.
  • Das Array wird nie gefiltert (zeigt eine Teilmenge des Arrays an).
  • Das Array wird nie neu angeordnet.
  • Das Array wird als Stack oder LIFO (last in, first out) verwendet. Mit anderen Worten, das Hinzufügen kann nur am Ende des Arrays (dh Push) und nur am letzten erfolgen Artikel kann immer entfernt werden (zB Pop).

Hätten wir stattdessen im obigen Snippet push das hinzugefügte Element am Ende des Arrays, wäre die Reihenfolge für jedes vorhandene Element immer korrekt.


Sehr schlecht

<tbody>
    {rows.map((row) => {
        return <ObjectRow key={Math.random()} />;
    })}
</tbody>

Während dieser Ansatz wahrscheinlich die Eindeutigkeit der Schlüssel garantiert, wird er always zwingen, jedes Element in der Liste erneut zu rendern, auch 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() zweimal dieselbe Zahl erzeugt.

Instabile Schlüssel (wie die von Math.random() erzeugten) führen dazu, dass viele Komponenteninstanzen und DOM-Knoten unnötigerweise neu erstellt werden, was zu Leistungseinbußen und zum Verlust des Status in untergeordneten Komponenten führen kann.


Sehr gut

<tbody>
    {rows.map((row) => {
        return <ObjectRow key={row.uniqueId} />;
    })}
</tbody>

Dies ist wahrscheinlich der beste Ansatz , da eine Eigenschaft verwendet wird, die für jedes Element im Dataset eindeutig ist. Wenn beispielsweise rows Daten enthält, die aus einer Datenbank abgerufen wurden, kann der Primärschlüssel der Tabelle verwendet werden ( das ist normalerweise eine automatisch inkrementierende Zahl).

Die beste Möglichkeit, einen Schlüssel auszuwählen, besteht darin, eine Zeichenfolge zu verwenden, die ein Listenelement unter seinen Geschwistern eindeutig identifiziert. Am häufigsten würden Sie IDs aus Ihren Daten als Schlüssel verwenden


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 von willkürlichen 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 wenn der Datensatz empfangen wird ( zB von props oder von einem asynchronen API-Aufruf), um dies nur zu tun einmal und nicht jedes Mal, wenn die Komponente neu gerendert wird. Es gibt bereits eine Handvoll Bibliotheken, die solche Schlüssel zur Verfügung stellen können. Hier ist ein Beispiel: React-Key-Index .

383
Chris

Warnung: Jedes untergeordnete Element in einem Array oder Iterator sollte eine eindeutige "Schlüssel" -Stütze haben.

Dies ist eine Warnung, da Array-Elemente, die wir durchlaufen, eine eindeutige Ähnlichkeit benötigen.

React behandelt das iterierende Komponenten-Rendering als Arrays.

Eine bessere Möglichkeit, dies zu beheben, ist die Bereitstellung eines Index für die Array-Elemente, die Sie durchlaufen. Beispiel: 

class UsersState extends Component
    {
        state = {
            users: [
                {name:"shashank", age:20},
                {name:"vardan", age:30},
                {name:"somya", age:40}
            ]
        }
    render()
        {
            return(
                    <div>
                        {
                            this.state.users.map((user, index)=>{
                                return <UserState key={index} age={user.age}>{user.name}</UserState>
                            })
                        }
                    </div>
                )
        }

index ist React eingebaute Requisiten.

2

Fügen Sie einfach den eindeutigen Schlüssel zu Ihren Komponenten hinzu 

data.map((marker)=>{
    return(
        <YourComponents 
            key={data.id}     // <----- unique key
        />
    );
})
1

Ich habe dies mit Hilfe von Guid für jeden Schlüssel wie folgt behoben:.

guid() {
    return this.s4() + this.s4() + '-' + this.s4() + '-' + this.s4() + '-' +
        this.s4() + '-' + this.s4() + this.s4() + this.s4();
}

s4() {
    return Math.floor((1 + Math.random()) * 0x10000)
        .toString(16)
        .substring(1);
}

Und dann diesen Wert den Markierungen zuweisen:

{this.state.markers.map(marker => (
              <MapView.Marker
                  key={this.guid()}
                  coordinate={marker.coordinates}
                  title={marker.title}
              />
          ))}
1
Archil Labadze

Check: key = undef !!!

Sie haben auch die Warnmeldung erhalten:

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

wenn Ihr Code richtig ist, aber wenn an

<ObjectRow key={someValue} />

someValue ist undefiniert !!! Bitte überprüfen Sie dies zuerst. Sie können Stunden sparen.

0
Gerd

Die einfachste Lösung, die ich gefunden habe, ist das Erstellen einer eindeutigen ID für jedes Element in der Liste. Es gibt 2 Bibliotheken, uniqid und uuid.

npm install uniqid

Dann in Ihrem Code:

import uniqueid from 'uniqid'
- OR if you use require:   var uniqid = require('uniqid');

Fügen Sie einfach einen Schlüssel zu dem HTML-Element hinzu, das es benötigt. Beachten Sie im folgenden Beispiel den key = {uniqueid ()}

{this.state.someList.map((item) => <li>{item}</li>)}
I changed to this:
{this.state.someList.map((item) => <li key={uniqueid()}>{item}</li>)}
0
MattC