Avvertenza: ogni figlio in un array o in un iteratore dovrebbe avere un prop "chiave" univoco. Controlla il metodo di rendering di "ListView"


102

Ho creato un'app con ReactNative sia per iOS che per Android con estensioneListView . Quando si popola la listview con un'origine dati valida, viene stampato il seguente avviso nella parte inferiore dello schermo:

Avvertenza: ogni figlio in un array o in un iteratore dovrebbe avere un prop "chiave" univoco. Controlla il metodo di rendering di ListView.

Qual è lo scopo di questo avvertimento? Dopo il messaggio si collegano a questa pagina , dove vengono discusse cose completamente diverse che non hanno nulla a che fare con React Native, ma con Reactjs basato sul web.

My ListView è costruito con queste istruzioni:

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}
            />

    );
}

Il mio DataSource è costituito da qualcosa come:

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

E le righe ListView vengono renderizzate con cose come:

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

Tutto funziona bene e come previsto, tranne l'avvertimento che mi sembra essere una totale assurdità.

L'aggiunta di una proprietà-chiave alla mia Classe "DetailItem" non ha risolto il problema.

Questo è ciò che verrà realmente passato a ListView come risultato di "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: 'xxxxxxxxx@hotmail.com',
I/ReactNativeJS( 1293):           headline: 'Email',
I/ReactNativeJS( 1293):           icon: 'fontawesome|envelope' },
I/ReactNativeJS( 1293):         { key: 6, type: 'moreInfo', text: '', headline: '', icon: '' } ] },

Come una chiave vede, ogni record ha una proprietà chiave. L'avvertimento esiste ancora.


1
Molto probabilmente DetailItemè necessario che tu abbia le chiavi. Se hanno già chiavi univoche, è necessario mostrare gli altri metodi di rendering ( renderHeader, renderDetailItem, renderSeparator). Funzionano bene e si aspettano fino a quando l'origine dati non viene modificata in qualche modo (le righe vengono rimosse, ad esempio), a quel punto React non saprà cosa farne quando non hanno un identificatore univoco.
Pete TNT

Cosa intendi con "chiavi"? Una proprietà chiamata "chiave"?
cancella il


Non lo risolve. Ho aggiunto una proprietà chiave alla mia struttura dati e aggiornato la domanda originale con dati più dettagliati. L'elenco dei dati normali, che risulta in DataSource, ha una chiave per ogni record. Questo avvertimento rimane.
cancellare il

Potrebbe provenire anche da altri metodi di rendering (renderHeader, renderDetailItem, renderSeparator)
Pete TNT

Risposte:


93

Ho avuto esattamente lo stesso problema di te per un po 'di tempo e dopo aver esaminato alcuni dei suggerimenti sopra, ho finalmente risolto il problema.

Si scopre (almeno per me comunque), avevo bisogno di fornire una chiave (un oggetto chiamato "chiave") al componente che sto restituendo dal mio metodo renderSeparator. L'aggiunta di una chiave al mio renderRow o renderSectionHeader non ha fatto nulla, ma aggiungerla a renderSeparator ha fatto scomparire l'avviso.

Spero che aiuti.


Nel mio caso, ho appena eliminato renderSeparator e spostato il mio <Separator> nel corpo del valore restituito da renderRow.
boatcoder

Stessa cosa qui per SectionList, è necessario aggiungere esplicitamente una proprietà con la chiave del nome per ciascuno itemper rendere felice RN.
LeOn - Han Li

1
Prima di leggere questo ho perso circa 8 ore a rintracciare quello che pensavo fosse un problema con i miei dati JSON. Se ci fosse uno stack overflow: taco: te ne darei uno!
hoekma

76

Devi fornire una chiave .

Prova a farlo nelle tue righe ListView se hai una proprietà chiave:

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

In caso contrario, prova ad aggiungere semplicemente l'elemento come chiave:

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

1
mi piace di più questa risposta, perché ha un codice così posso copiare XD
Jovylle Bermudez

34

Puoi anche usare il conteggio delle iterazioni (i) come key:

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

2
Potrebbe non funzionare quando l'array cambia. Questa risposta lo spiega con un esempio: stackoverflow.com/a/43892905/960857
Chris

21

Cambia il tuo codice da:

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

Per:

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

Quindi risolto.


13

Aggiungi una 'chiave' prop al componente radice di rendering dell'elenco.

<ScrollView>
      <List>
          {this.state.nationalities.map((prop, key) => {
             return (
               <ListItem key={key}>
                  <Text>{prop.name}</Text>
               </ListItem>
             );
          })}
      </List>
</ScrollView>

6

Questo avviso viene visualizzato quando non aggiungi una chiave agli elementi dell'elenco.

I tasti aiutano React a identificare quali elementi sono stati modificati, aggiunti o rimossi. Le chiavi dovrebbero essere fornite agli elementi all'interno dell'array per dare agli elementi un'identità stabile:

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

Il modo migliore per scegliere una chiave è utilizzare una stringa che identifichi in modo univoco un elemento dell'elenco tra i suoi fratelli. Molto spesso useresti gli ID dei tuoi dati come chiavi:

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

Quando non disponi di ID stabili per gli elementi sottoposti a rendering, puoi utilizzare l'indice dell'elemento come chiave come ultima risorsa

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

4

L'ho risolto aggiungendo una proprietà al componente renderSeparator, il codice è qui:

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

Le parole chiave di questo avviso sono "unique", sectionID + rowID restituiscono un valore univoco in ListView.


4

Controlla: key = undef !!!

Hai anche ricevuto il messaggio di avviso:

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

se il tuo codice è completo correttamente, ma se attivo

<MyComponent key={someValue} />

someValue non è definito !!! Per favore controlla prima questo. Puoi risparmiare ore.


3

Supponendo che il metodo renderDetailItem abbia la seguente firma ...

(rowData, sectionID, rowID, highlightRow) 

Prova a farlo ...

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

3

Il codice specifico che ho usato per risolvere questo problema era:

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

Includo il codice specifico perché è necessario che le chiavi siano univoche, anche per i separatori. Se fai qualcosa di simile, ad esempio, se lo imposti su una costante, otterrai solo un altro fastidioso errore sul riutilizzo delle chiavi. Se non conosci JSX, costruire il callback a JS per eseguire le varie parti può essere un bel problema.

E su ListView, ovviamente allegando questo:

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

Ringraziamo coldbuffet e Nader Dabit che mi hanno indicato questa strada.


2

Qui si basa sulla mia comprensione. Spero sia utile. Dovrebbe eseguire il rendering di un elenco di tutti i componenti come esempio dietro. Il tag radice di ogni componente deve avere un'estensione key. Non deve essere unico. Non può essere key=0, key='0'ecc. Sembra che la chiave sia inutile.

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

0

Sembra che entrambe le condizioni siano soddisfatte, forse la chiave ("contatto") è il 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') );
}

no, il contatto non è una chiave. Nel frattempo ho aggiunto una proprietà reale chiamata "chiave" alla mia struttura dati e ho aggiornato la mia domanda passando dati più dettagliati. Niente aiuta. L'avvertimento rimane.
cancella il


0

La cosa che mi ha fatto inciampare su questo problema è stato che ho pensato che la necessità di una chiave applicata a quelli che sembrano elementi HTML "reali" o DOM anziché agli elementi JSX che ho definito.

Ovviamente con React stiamo lavorando con un DOM virtuale, quindi gli elementi React JSX che definiamo <MyElement>sono importanti tanto quanto gli elementi che sembrano veri elementi HTML DOM <div>.

Ha senso?


0

Nel mio caso, stavo usando la vista "Scheda" di React dell'interfaccia utente semantica. Dopo aver aggiunto una chiave a ogni carta che ho costruito, l'avvertimento è scomparso, ad esempio:

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

-1

Se stai usando l' <Fade in>elemento per un'applicazione reattiva, aggiungi anche l' key={}attributo o vedrai un errore nella console.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.