Advertencia: Cada niño en una matriz o iterador debe tener un accesorio de "clave" único. Verifique el método de renderizado de `ListView`

102

Creé una aplicación con ReactNative tanto para iOS como para Android con una extensiónListView . Al llenar la vista de lista con una fuente de datos válida, se imprime la siguiente advertencia en la parte inferior de la pantalla:

Advertencia: Cada niño en una matriz o iterador debe tener una propiedad de "clave" única. Compruebe el método de renderizado de ListView.

¿Cuál es el propósito de esta advertencia? Después del mensaje, enlazan a esta página , donde se discuten cosas diferentes que no tienen nada que ver con react native, sino con reactjs basados ​​en web.

Mi ListView está construido con esas declaraciones:

render() {
    var store = this.props.store;

    return (

        <ListView
            dataSource={this.state.dataSource}
            renderHeader={this.renderHeader.bind(this)}
            renderRow={this.renderDetailItem.bind(this)}
            renderSeparator={this.renderSeparator.bind(this)}
            style={styles.listView}
            />

    );
}

Mi fuente de datos consta de algo como:

    var detailItems = [];

    detailItems.push( new DetailItem('plain', store.address) );
    detailItems.push( new DetailItem('map', '') );

    if(store.telefon) {
        detailItems.push( new DetailItem('contact', store.telefon, 'Anrufen', 'fontawesome|phone') );
    }
    if(store.email) {
        detailItems.push( new DetailItem('contact', store.email, 'Email', 'fontawesome|envelope') );
    }
    detailItems.push( new DetailItem('moreInfo', '') );

    this.setState({
        dataSource: this.state.dataSource.cloneWithRows(detailItems)
    });

Y las ListView-Rows se representan con cosas como:

        return (
            <TouchableHighlight underlayColor='#dddddd'>
                <View style={styles.infoRow}>
                    <Icon
                                name={item.icon}
                                size={30}
                                color='gray'
                                style={styles.contactIcon}
                                />
                    <View style={{ flex: 1}}>
                        <Text style={styles.headline}>{item.headline}</Text>
                        <Text style={styles.details}>{item.text}</Text>
                    </View>
                    <View style={styles.separator}/>
                </View>
            </TouchableHighlight>
        );

Todo funciona bien y como se esperaba, excepto la advertencia que me parece una completa tontería.

Agregar una propiedad clave a mi clase "DetailItem" no resolvió el problema.

Esto es, lo que realmente se pasará a ListView como resultado de "cloneWithRows":

_dataBlob: 
I/ReactNativeJS( 1293):    { s1: 
I/ReactNativeJS( 1293):       [ { key: 2,
I/ReactNativeJS( 1293):           type: 'plain',
I/ReactNativeJS( 1293):           text: 'xxxxxxxxxx',
I/ReactNativeJS( 1293):           headline: '',
I/ReactNativeJS( 1293):           icon: '' },
I/ReactNativeJS( 1293):         { key: 3, type: 'map', text: '', headline: '', icon: '' },
I/ReactNativeJS( 1293):         { key: 4,
I/ReactNativeJS( 1293):           type: 'contact',
I/ReactNativeJS( 1293):           text: '(xxxx) yyyyyy',
I/ReactNativeJS( 1293):           headline: 'Anrufen',
I/ReactNativeJS( 1293):           icon: 'fontawesome|phone' },
I/ReactNativeJS( 1293):         { key: 5,
I/ReactNativeJS( 1293):           type: 'contact',
I/ReactNativeJS( 1293):           text: '[email protected]',
I/ReactNativeJS( 1293):           headline: 'Email',
I/ReactNativeJS( 1293):           icon: 'fontawesome|envelope' },
I/ReactNativeJS( 1293):         { key: 6, type: 'moreInfo', text: '', headline: '', icon: '' } ] },

Como ve una clave, cada registro tiene una propiedad clave. La advertencia aún existe.

Eliminar
fuente
1
Lo más probable DetailItemes que sus s necesiten tener claves. Si ya tienen claves únicas, necesita mostrar los otros métodos de renderizado ( renderHeader, renderDetailItem, renderSeparator). Funcionan bien y se esperan hasta que la fuente de datos se modifique de alguna manera (las filas se eliminan, por ejemplo), momento en el que React no sabrá qué hacer con ellos cuando no tengan un identificador único.
Pete TNT
¿Qué quieres decir con "llaves"? ¿Una propiedad llamada "clave"?
eliminar el
No lo resuelve. Agregué una propiedad clave a mi estructura de datos y actualicé la pregunta original con datos más detallados. Enumerar los datos sin formato, que resultan en el origen de datos, tiene una clave por cada registro. Esta advertencia permanece.
eliminar el
También podría provenir de los otros métodos de renderización (renderHeader, renderDetailItem, renderSeparator)
Pete TNT

Respuestas:

93

He tenido exactamente el mismo problema que tú desde hace un tiempo, y después de ver algunas de las sugerencias anteriores, finalmente resolví el problema.

Resulta que (al menos para mí de todos modos), necesitaba proporcionar una clave (una propiedad llamada 'clave') al componente que estoy devolviendo de mi método renderSeparator. Agregar una clave a mi renderRow o renderSectionHeader no hizo nada, pero agregarlo a renderSeparator hizo que la advertencia desapareciera.

Espero que ayude.

buffet frío
fuente
En mi caso, acabo de eliminar renderSeparator y moví mi <Separator> al cuerpo del valor de retorno de renderRow.
boatcoder
Lo mismo aquí para el SectionList, tiene que agregar explícitamente una propiedad con la clave de nombre para cada uno itempara hacer feliz a RN.
LeOn - Han Li
1
Antes de leer esto, perdí aproximadamente 8 horas rastreando lo que pensé que era un problema con mis datos JSON. Si hubiera un desbordamiento de pila: taco: ¡te daría uno!
hoekma
76

Debe proporcionar una clave .

Intente hacer esto en sus ListView Rows si tiene una propiedad clave:

<TouchableHighlight key={item.key} underlayColor='#dddddd'>

De lo contrario, intente simplemente agregar el elemento como clave:

<TouchableHighlight key={item} underlayColor='#dddddd'>
Nader Dabit
fuente
1
Me gusta más esta respuesta, porque tiene un código para que pueda copiar XD
Jovylle Bermudez
34

También puede utilizar el recuento de iteraciones (i) como key:

render() {
    return (
      <ol>
        {this.props.results.map((result, i) => (
          <li key={i}>{result.text}</li>
        ))}
      </ol>
    );
}
Agu Dondo
fuente
2
Es posible que esto no funcione cuando cambia la matriz. Esta respuesta lo explica con un ejemplo: stackoverflow.com/a/43892905/960857
Chris
21

Cambie su código de:

render() {
    return (
      <ol>
        {this.props.results.map((result) => (
          <li>{result.text}</li>
        ))}
      </ol>
    );
}

A:

render() {
    return (
      <ol>
        {this.props.results.map((result) => (
          <li key={result.id}>{result.text}</li>
        ))}
      </ol>
    );
}

Luego resuelto.

micsay
fuente
13

Agregue una 'clave' de apoyo al componente raíz de representación de la lista.

<ScrollView>
      <List>
          {this.state.nationalities.map((prop, key) => {
             return (
               <ListItem key={key}>
                  <Text>{prop.name}</Text>
               </ListItem>
             );
          })}
      </List>
</ScrollView>
Espíritu de solución
fuente
6

Esta advertencia se produce cuando no agrega una clave a los elementos de su lista. Según react js Docs -

Las claves ayudan a React a identificar qué elementos han cambiado, se han agregado o se han eliminado. Se deben dar claves a los elementos dentro de la matriz para darles una identidad estable:

const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
  <li key={number.toString()}>
    {number}
  </li>
);

La mejor forma de elegir una clave es utilizar una cadena que identifique de forma única un elemento de la lista entre sus hermanos. La mayoría de las veces usaría ID de sus datos como claves:

const todoItems = todos.map((todo) =>
  <li key={todo.id}>
    {todo.text}
  </li>
);

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>
);
Jagrati
fuente
4

Lo arreglé agregando una propiedad para renderSeparator Component, el código está aquí:

_renderSeparator(sectionID,rowID){
    return (
        <View style={styles.separatorLine} key={"sectionID_"+sectionID+"_rowID_"+rowID}></View>
    );
}

Las palabras clave de esta advertencia son "únicas", sectionID + rowID devuelven un valor único en ListView.

bocai
fuente
4

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

<MyComponent key={someValue} />

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

Gerd
fuente
3

Suponiendo que el método renderDetailItem tiene la siguiente firma ...

(rowData, sectionID, rowID, highlightRow) 

Intenta hacer esto ...

<TouchableHighlight key={rowID} underlayColor='#dddddd'>
peligroso
fuente
3

El código específico que utilicé para solucionar esto fue:

  renderSeparator(sectionID, rowID, adjacentRowHighlighted) {
    return (
      <View style={styles.separator} key={`${sectionID}-${rowID}`}/>
    )
  }

Incluyo el código específico porque necesita que las claves sean únicas, incluso para los separadores. Si hace algo similar, por ejemplo, si establece esto en una constante, obtendrá otro error molesto sobre la reutilización de claves. Si no conoce JSX, construir la devolución de llamada a JS para ejecutar las distintas partes puede ser bastante complicado.

Y en ListView, obviamente adjuntando esto:

<ListView
  style={styles.listview}
  dataSource={this.state.dataSource}
  renderRow={this.renderRow.bind(this)}
  renderSeparator={this.renderSeparator.bind(this)}
  renderSectionHeader={this.renderSectionHeader.bind(this)}/>

Gracias a Coldbuffet y Nader Dabit, quienes me señalaron este camino.

Selly
fuente
2

Aquí se basa en mi entendimiento. Ojalá sea útil. Se supone que debe representar una lista de cualquier componente como el ejemplo de detrás. La etiqueta raíz de cada componente debe tener una extensión key. No tiene por qué ser único. No puede ser key=0, key='0'etc. Parece que la clave es inútil.

render() {
    return [
        (<div key={0}> div 0</div>),
        (<div key={1}> div 2</div>),
        (<table key={2}><tbody><tr><td> table </td></tr></tbody></table>),
        (<form key={3}> form </form>),
    ];
}
caot
fuente
0

Parece que se cumplen ambas condiciones, quizás la clave ('contacto') sea el problema

 if(store.telefon) {
    detailItems.push( new DetailItem('contact', store.telefon, 'Anrufen', 'fontawesome|phone') );
}
if(store.email) {
    detailItems.push( new DetailItem('contact', store.email, 'Email', 'fontawesome|envelope') );
}
Shiva Acharjee
fuente
no, el contacto no es una clave. Entre tanto, agregué una propiedad real llamada "clave" a mi estructura de datos y actualicé mi pregunta pasando datos más detallados. Nada ayuda. La advertencia permanece.
eliminar el
0

Lo que me hizo tropezar con este problema fue que pensé que la necesidad de una clave se aplicaba a lo que parece "real" o elementos DOM HTML en contraposición a los elementos JSX que he definido.

Por supuesto, con React estamos trabajando con un DOM virtual, por lo que los elementos de React JSX que definimos <MyElement>son tan importantes para él como los elementos que parecen elementos DOM HTML reales como<div> .

¿Tiene sentido?

Ross Attrill
fuente
0

En mi caso, estaba usando la vista "Tarjeta" de Semantic UI React. Una vez que agregué una clave a cada tarjeta que construí, la advertencia desapareció, por ejemplo:

return (
        <Card fluid key={'message-results-card'}>
          ...
        </Card>
)
Michael Bordash
fuente
-1

Si está utilizando el <Fade in>elemento para una aplicación de reacción, agregue un key={}atributo también o verá un error en la consola.

Reid McCulloch
fuente