Comprender las claves únicas para los niños de la matriz en React.js

Estoy construyendo un componente React que acepta una fuente de datos JSON y crea una tabla ordenable.
Cada una de las filas de datos dinámicos tiene una clave única asignada, pero sigo recibiendo un error de:

Each child in an array should have a unique "key" prop.
Check the render method of TableComponent.

Mi TableComponentmétodo de renderizado devuelve:

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

El TableHeadercomponente es una sola fila y también tiene una clave única asignada.

Cada uno rowde rowsse construye a partir de un componente con una clave única:

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

Y se TableRowItemve así:

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>
    )
  }
});

¿Qué está causando el error de apoyo de clave única?

Answer

Debe agregar una clave a cada niño , así como a cada elemento dentro de los niños .

De esta manera, React puede manejar el cambio mínimo de DOM.

En su código, cada uno <TableRowItem key={item.id} data={item} columns={columnNames}/>está tratando de representar algunos niños dentro de ellos sin una clave.

Mira este ejemplo .

Intente eliminar key={i}del <b></b>elemento dentro de los div (y verifique la consola).

En la muestra, si no le damos una clave al <b>elemento y queremos actualizar solo el object.city, React necesita volver a renderizar toda la fila en lugar de solo el elemento.

Aquí está el código:

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);

La respuesta publicada por @Chris en la parte inferior entra en muchos más detalles que esta respuesta. Eche un vistazo a https://stackoverflow.com/a/43892905/2325522

Reaccionar documentación sobre la importancia de las claves en la reconciliación: claves

¡Tenga cuidado al iterar sobre matrices!

Es un error común pensar que usar el índice del elemento en la matriz es una forma aceptable de suprimir el error con el que probablemente esté familiarizado:

Each child in an array should have a unique "key" prop.

Sin embargo, en muchos casos no lo es. Este es un anti-patrón que en algunas situaciones puede conducir a un comportamiento no deseado .


Entendiendo el keyprop

React usa el keyaccesorio para comprender la relación componente-elemento DOM, que luego se usa para el proceso de reconciliación . Por eso es muy importante que la clave siempre sigue siendo único , de lo contrario hay una buena probabilidad de React será mezclar los elementos y mutar el incorrecto. También es importante que estas claves permanezcan estáticas durante todas las repeticiones para mantener el mejor rendimiento.

Dicho esto, no siempre es necesario aplicar lo anterior, siempre que se sepa que la matriz es completamente estática. Sin embargo, se recomienda aplicar las mejores prácticas siempre que sea posible.

Un desarrollador de React dijo en este número de GitHub :

  • key is not really about performance, it's more about identity (which in turn leads to better performance). randomly assigned and changing values are not identity
  • We can't realistically provide keys [automatically] without knowing how your data is modeled. I would suggest maybe using some sort of hashing function if you don't have ids
  • We already have internal keys when we use arrays, but they are the index in the array. When you insert a new element, those keys are wrong.

En resumen, a keydebería ser:

  • Único : una clave no puede ser idéntica a la de un componente hermano .
  • Estático : una clave nunca debería cambiar entre renderizados.

Usando el keyaccesorio

Según la explicación anterior, estudie cuidadosamente las siguientes muestras e intente implementar, cuando sea posible, el enfoque recomendado.


Malo (potencialmente)

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

Este es posiblemente el error más común que se observa al iterar sobre una matriz en React. Este enfoque no es técnicamente "incorrecto" , es simplemente ... "peligroso" si no sabe lo que está haciendo. Si está iterando a través de una matriz estática, este es un enfoque perfectamente válido (por ejemplo, una matriz de enlaces en su menú de navegación). Sin embargo, si agrega, elimina, reordena o filtra elementos, debe tener cuidado. Eche un vistazo a esta explicación detallada en la documentación oficial.

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>

En este fragmento, estamos usando una matriz no estática y no nos estamos restringiendo a usarla como una pila. Este es un enfoque inseguro (verá por qué). Tenga en cuenta que a medida que agregamos elementos al principio de la matriz (básicamente unshift), el valor de cada uno <input>permanece en su lugar. ¿Por qué? Porque keyno identifica de forma única cada artículo.

En otras palabras, al principio Item 1tiene key={0}. Cuando agregamos el segundo elemento, el elemento superior se convierte en Item 2, seguido Item 1del segundo elemento. Sin embargo, ahora Item 1tiene key={1}y key={0}ya no . En cambio, Item 2ahora tiene key={0}!!

Como tal, React cree que los <input>elementos no han cambiado, ¡porque la Itemtecla with 0está siempre en la parte superior!

Entonces, ¿por qué este enfoque solo a veces es malo?

Este enfoque solo es arriesgado si la matriz se filtra, reorganiza o se agregan / eliminan elementos de alguna manera. Si siempre es estático, entonces es perfectamente seguro de usar. Por ejemplo, un menú de navegación como ["Home", "Products", "Contact us"]se puede iterar de forma segura con este método porque probablemente nunca agregará nuevos enlaces ni los reorganizará.

En resumen, aquí es cuando puede usar el índice de manera segura como key:

  • La matriz es estática y nunca cambiará.
  • La matriz nunca se filtra (muestra un subconjunto de la matriz).
  • La matriz nunca se reordena.
  • La matriz se utiliza como una pila o LIFO (último en entrar, primero en salir). En otras palabras, solo se puede agregar al final de la matriz (es decir, presionar), y solo se puede eliminar el último elemento (es decir, pop).

Si en cambio, en el fragmento anterior, hubiéramos empujado el elemento agregado al final de la matriz, el orden de cada elemento existente siempre sería correcto.


Muy mal

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

Si bien este enfoque probablemente garantizará la unicidad de las claves, siempre obligará a reaccionar para volver a representar cada elemento de la lista, incluso cuando esto no sea necesario. Esta es una muy mala solución ya que afecta enormemente el rendimiento. Sin mencionar que no se puede excluir la posibilidad de una colisión de claves en el caso de que se Math.random()produzca el mismo número dos veces.

Unstable keys (like those produced by Math.random()) will cause many component instances and DOM nodes to be unnecessarily recreated, which can cause performance degradation and lost state in child components.


Muy bien

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

Este es posiblemente el mejor enfoque porque utiliza una propiedad que es única para cada elemento del conjunto de datos. Por ejemplo, si rowscontiene datos extraídos de una base de datos, se podría usar la clave principal de la tabla ( que normalmente es un número que se incrementa automáticamente ).

The best way to pick a key is to use a string that uniquely identifies a list item among its siblings. Most often you would use IDs from your data as keys


Bien

componentWillMount() {
  let rows = this.props.rows.map(item => { 
    return {uid: SomeLibrary.generateUniqueID(), value: item};
  });
}

...

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

Este también es un buen enfoque. Si su conjunto de datos no contiene ningún dato que garantice la unicidad ( por ejemplo, una matriz de números arbitrarios ), existe la posibilidad de una colisión de claves. En tales casos, es mejor generar manualmente un identificador único para cada elemento en el conjunto de datos antes de iterar sobre él. Preferiblemente al montar el componente o cuando se recibe el conjunto de datos ( por ejemplo, desde propso desde una llamada API asíncrona ), para hacer esto solo una vez , y no cada vez que el componente se vuelve a renderizar. Ya hay un puñado de bibliotecas que pueden proporcionarle dichas claves. Aquí hay un ejemplo: react-key-index .

Esto puede ayudar o no a alguien, pero podría ser una referencia rápida. Esto también es similar a todas las respuestas presentadas anteriormente.

Tengo muchas ubicaciones que generan listas usando la estructura a continuación:

return (
    {myList.map(item => (
       <>
          <div class="some class"> 
             {item.someProperty} 
              ....
          </div>
       </>
     )}
 )
         

Después de un poco de prueba y error (y algunas frustraciones), agregar una propiedad clave al bloque más externo lo resolvió. Además, tenga en cuenta que la <>etiqueta ahora se reemplaza con la <div>etiqueta ahora.

return (
  
    {myList.map((item, index) => (
       <div key={index}>
          <div class="some class"> 
             {item.someProperty} 
              ....
          </div>
       </div>
     )}
 )

Por supuesto, he estado usando ingenuamente el índice iterativo (índice) para completar el valor de la clave en el ejemplo anterior. Idealmente, usaría algo que sea exclusivo del elemento de la lista.

Comprobar: clave = indef !!!

También recibiste el mensaje de advertencia:

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

si su código está completo correctamente, pero si está activado

<ObjectRow key={someValue} />

someValue no está definido !!! Por favor, compruebe esto primero. Puede ahorrar horas.

Simplemente agregue la clave única a sus componentes

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

Advertencia: Cada niño en una matriz o iterador debe tener una propiedad de "clave" única.

Esta es una advertencia, ya que los elementos de la matriz sobre los que vamos a iterar necesitarán una semejanza única.

React maneja la representación de componentes iterativos como matrices.

La mejor manera de resolver esto es proporcionar un índice en los elementos de la matriz sobre los que va a iterar, por ejemplo:

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 es un accesorio integrado de React.

Debe utilizar un valor único para cada clave secundaria de tbodydonde

  • el valor no puede no ser idéntico (mismo) a su hermano
  • no debería cambiar entre renders

Por ejemplo, el valor de la clave puede ser el ID de la base de datos o UUID (Identificador único universal) .

Aquí las claves se manejan manualmente:

<tbody>
  {rows.map((row) => <ObjectRow key={row.uuid} />)}
</tbody>

También puede dejar que React maneje las claves usando React.Children.toArray

<tbody>
  {React.Children.toArray(rows.map((row) => <ObjectRow />))}
</tbody>

En ReactJS, si está renderizando una matriz de elementos, debe tener una clave única para cada uno de esos elementos. Normalmente, ese tipo de situaciones están creando una lista.

Ejemplo:

function List() {
  const numbers = [0,1,2,3];
 
  return (
    <ul>{numbers.map((n) => <li> {n} </li>)}</ul>
  );
}

 ReactDOM.render(
  <List />,
  document.getElementById('root')
);

En el ejemplo anterior, crea una lista dinámica usando una lietiqueta, por lo que dado que la lietiqueta no tiene una clave única, muestra un error.

Después de arreglado:

function List() {
  const numbers = [0,1,2,3];
 
  return (
    <ul>{numbers.map((n) => <li key={n}> {n} </li>)}</ul>
  );
}

 ReactDOM.render(
  <List />,
  document.getElementById('root')
);

Solución alternativa cuando usa map cuando no tiene una clave única (esto no es recomendado por react eslint ):

function List() {
  const numbers = [0,1,2,3,4,4];
 
  return (
    <ul>{numbers.map((n,i) => <li key={i}> {n} </li>)}</ul>
  );
}

 ReactDOM.render(
  <List />,
  document.getElementById('root')
);

Ejemplo en vivo: https://codepen.io/spmsupun/pen/wvWdGwG

La mejor solución para definir una clave única en reaccionar: dentro del mapa, inicializó el nombre de la publicación, luego definió la clave por clave = {publicación.id} o en mi código, verá que defino el elemento de nombre y luego defino la clave por clave = {elemento.id }:

<div className="container">
                {posts.map(item =>(

                    <div className="card border-primary mb-3" key={item.id}>
                        <div className="card-header">{item.name}</div>
                    <div className="card-body" >
                <h4 className="card-title">{item.username}</h4>
                <p className="card-text">{item.email}</p>
                    </div>
                  </div>
                ))}
            </div>

Me encontré con este mensaje de error porque <></>se devolvieron algunos elementos de la matriz cuando, en su lugar, nulles necesario devolverlos.

Tenía una clave única, solo tenía que pasarla como un accesorio como este:

<CompName key={msg._id} message={msg} />

Esta página fue útil:

https://reactjs.org/docs/lists-and-keys.html#keys

Cuando no tenga ID estables para los elementos renderizados, puede usar el índice del elemento como clave como último recurso:

const todoItems = todos.map((todo, index) =>
// Only do this if items have no stable IDs
   <li key={index}>
      {todo.text}
   </li>
);

Consulte Lista y claves - Reaccionar

Esta es una advertencia, pero abordar esto hará que Reacts se vuelva mucho más RÁPIDO ,

Esto se Reactdebe a que es necesario identificar de forma única cada elemento de la lista. Digamos que si el estado de un elemento de esa lista cambia en Reacts, Virtual DOMentonces React necesita averiguar qué elemento se cambió y en qué parte del DOM debe cambiar para que el DOM del navegador esté sincronizado con el DOM virtual de Reacts.

Como solución, simplemente introduzca un keyatributo en cada lietiqueta. Este keydebe ser un valor único para cada elemento.

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

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

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

Esto solucionará el problema.

En mi caso, establezca id para etiquetar

<tbody key={i}>

El problema esta resuelto.

If you are getting error like :

> index.js:1 Warning: Each child in a list should have a unique "key" prop.

Check the render method of `Home`. See https://reactjs.org/link/warning-keys for more information.

Then Use inside map function like:

  {classes.map((user, index) => (
              <Card  **key={user.id}**></Card>
  ))}`enter code here`

Enfrenté un problema similar pero no exacto. Probé todas las soluciones posibles y no pude deshacerme de ese error

Each child in an array should have a unique "key" prop.

Luego intenté abrirlo en un host local diferente. No sé cómo, ¡pero funcionó!

Este es un ejemplo simple, he usado una condición de reacción con &&primero y luego con el mapa, en el que he agregado la keyidentificación del usuario para asegurarme de que sea única

 <tbody>
                                    {users &&
                                    users.map((user) => {
                                        return <tr key={user._id}>
                                            <td>{user.username}</td>
                                            <td><input
                                                name="isGoing"
                                                type="checkbox"
                                                checked={user.isEnabled}
                                                onChange={handleInputChangeNew}/></td>
                                            <td>{user.role.roleTitle} - {user.role.department.departmentName}</td>
                                            {/*<td className="text-right">
                                                    <Button>
                                                        ACTION
                                                    </Button>
                                                </td>*/}
                                        </tr>
                                    })
                                    }


                                    </tbody>

No sigo la explicación detallada, pero la clave para esta respuesta es "clave", simplemente coloque el atributo clave en su etiqueta y asegúrese de que cada vez que repita le dé un valor único.

#asegúrese de que el valor de la clave no entre en conflicto con otros

Ejemplo

<div>
        {conversation.map(item => (
          <div key={item.id  } id={item.id}>
          </div>
        ))}
      </div>

donde la conversación es una matriz similar a la siguiente:

  const conversation = [{id:"unique"+0,label:"OPEN"},{id:"unique"+1,label:"RESOLVED"},{id:"unique"+2,label:"ARCHIVED"},
   ]

Arreglé esto usando Guid para cada clave de esta manera: Generando Guid:

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);
}

Y luego asignando este valor a los marcadores:

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