Aviso: Cada filho em uma matriz ou iterador deve ter uma prop “chave” exclusiva. Verifique o método de renderização de `ListView`

97

Construí um aplicativo com ReactNative para iOS e Android com um ListView. Ao preencher o listview com uma fonte de dados válida, o seguinte aviso é impresso na parte inferior da tela:

Aviso: cada filho em uma matriz ou iterador deve ter uma prop "chave" exclusiva. Verifique o método de renderização de ListView.

Qual é o propósito deste aviso? Após a mensagem, eles são direcionados para esta página , onde são discutidas coisas completamente diferentes que não têm nada a ver com a reação nativa, mas com reações baseadas na web.

My ListView é construído com essas instruções:

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

    );
}

Minha fonte de dados consiste em 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)
    });

E as ListView-Rows são renderizadas com coisas 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>
        );

Tudo funciona bem e conforme o esperado, exceto o aviso que parece ser um disparate completo para mim.

Adicionar uma propriedade-chave à minha classe "DetailItem" não resolveu o problema.

Isto é, o que realmente será passado para o 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 uma chave vê, cada registro possui uma propriedade chave. O aviso ainda existe.

excluir
fonte
1
Provavelmente, você DetailItemprecisa de chaves. Se eles já têm chaves exclusivas, você precisa mostrar os outros métodos de renderização ( renderHeader, renderDetailItem, renderSeparator). Eles estão funcionando bem e são esperados até que a fonte de dados seja modificada de alguma forma (as linhas são removidas, por exemplo), momento em que o React não saberá o que fazer com eles se não tiverem um identificador exclusivo.
Pete TNT
O que você quer dizer com "chaves"? Uma propriedade chamada "chave"?
excluir
Isso não resolve. Eu adicionei uma propriedade chave à minha estrutura de dados e atualizei a pergunta original com dados mais detalhados. Os dados simples de listagem, que resultam no DataSource, têm uma chave para cada registro. Este aviso permanece.
excluir
Pode vir de outros métodos de renderização também (renderHeader, renderDetailItem, renderSeparator)
Pete TNT

Respostas:

88

Eu tenho exatamente o mesmo problema que você há algum tempo e, depois de examinar algumas das sugestões acima, finalmente resolvi o problema.

Acontece que (pelo menos para mim de qualquer maneira), eu precisava fornecer uma chave (um prop chamado 'chave') para o componente que estou retornando do meu método renderSeparator. Adicionar uma chave a meu renderRow ou renderSectionHeader não fez nada, mas adicioná-la a renderSeparator fez o aviso desaparecer.

Espero que ajude.

bufê frio
fonte
No meu caso, acabei de excluir renderSeparator e movi meu <Separator> para o corpo do valor de retorno renderRow.
boatcoder
A mesma coisa aqui para o SectionList, tem que adicionar explicitamente uma propriedade com a chave do nome para cada um itempara deixar RN feliz.
LeOn - Han Li
1
Antes de ler isso, perdi cerca de 8 horas rastreando o que pensava ser um problema com meus dados JSON. Se houver um estouro de pilha: taco: Eu te daria um!
hoekma
75

Você precisa fornecer uma chave .

Tente fazer isso em suas ListView Rows se você tiver uma propriedade-chave:

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

Caso contrário, tente apenas adicionar o item como a chave:

<TouchableHighlight key={item} underlayColor='#dddddd'>
Nader Dabit
fonte
1
eu gosto mais dessa resposta, porque tem código para que eu possa copiar XD
Jovylle Bermudez
33

Você também pode usar a contagem de iteração (i) como key:

render() {
    return (
      <ol>
        {this.props.results.map((result, i) => (
          <li key={i}>{result.text}</li>
        ))}
      </ol>
    );
}
Agu Dondo
fonte
2
Isso pode não funcionar quando a matriz muda. Esta resposta explica com um exemplo: stackoverflow.com/a/43892905/960857
Chris
20

Altere seu código de:

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

Para:

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

Então resolvido.

micsay
fonte
13

Adicione uma 'chave' prop ao componente raiz de renderização da lista.

<ScrollView>
      <List>
          {this.state.nationalities.map((prop, key) => {
             return (
               <ListItem key={key}>
                  <Text>{prop.name}</Text>
               </ListItem>
             );
          })}
      </List>
</ScrollView>
Espírito de Solução
fonte
6

Este aviso surge quando você não adiciona uma chave aos itens da lista. De acordo com o react js Docs -

As teclas ajudam o React a identificar quais itens foram alterados, adicionados ou removidos. As chaves devem ser fornecidas aos elementos dentro da matriz para dar aos elementos uma identidade estável:

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

A melhor maneira de escolher uma chave é usar uma string que identifique exclusivamente um item da lista entre seus irmãos. Na maioria das vezes, você usaria IDs de seus dados como chaves:

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

Quando você não tem IDs estáveis ​​para itens renderizados, você pode usar o índice do item como uma chave 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
fonte
4

Eu corrigi ao adicionar uma propriedade ao componente renderSeparator, o código está aqui:

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

As palavras-chave desse aviso são "exclusivo", sectionID + rowID retornam um valor exclusivo em ListView.

bocai
fonte
3

Supondo que o método renderDetailItem tenha a seguinte assinatura ...

(rowData, sectionID, rowID, highlightRow) 

Tente fazer isso ...

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

O código específico que usei para corrigir isso foi:

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

Estou incluindo o código específico porque você precisa que as chaves sejam exclusivas - mesmo para separadores. Se você fizer algo semelhante, por exemplo, se definir isso como uma constante, receberá apenas outro erro irritante sobre a reutilização de chaves. Se você não conhece JSX, construir um retorno de chamada para JS para executar as várias partes pode ser uma dor de cabeça.

E no ListView, obviamente anexando isto:

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

Crédito para coldbuffet e Nader Dabit que me indicou este caminho.

Selly
fonte
3

Verifique: key = undef !!!

Você também recebeu a mensagem de aviso:

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

se o seu código estiver completo, mas se estiver

<MyComponent key={someValue} />

someValue is undefined !!! Verifique isso primeiro. Você pode economizar horas.

Gerd
fonte
2

Aqui está baseado no meu entendimento. Espero que seja útil. Ele deve renderizar uma lista de todos os componentes como exemplo. A tag raiz de cada componente precisa ter um key. Não precisa ser único. Não pode ser key=0, key='0'etc. Parece que a chave é 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
fonte
0

Parece que ambas as condições foram atendidas, talvez a chave ('contato') seja o 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
fonte
não, o contato não é uma chave. No meio, adicionei uma propriedade chamada "chave" à minha estrutura de dados e atualizei minha pergunta passando dados mais detalhados. Nada ajuda. O aviso permanece.
excluir
0

O que me fez tropeçar nesse problema foi que eu pensei que a necessidade de uma chave se aplicava ao que parece ser "real" ou elementos HTML DOM, em oposição aos elementos JSX que eu defini.

É claro que com o React estamos trabalhando com um DOM virtual, portanto, os elementos React JSX que definimos <MyElement>são tão importantes para ele quanto os elementos que se parecem com os elementos HTML DOM reais <div>.

Isso faz sentido?

Ross Attrill
fonte
0

No meu caso, eu estava usando a visualização "Cartão" do Semantic UI React. Depois de adicionar uma chave a cada cartão que construí, o aviso desapareceu, por exemplo:

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

Se você estiver usando o <Fade in>elemento para um aplicativo de reação, adicione o key={}atributo nele também ou você verá um erro no console.

Reid McCulloch
fonte