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: 'xxxxxxxxx@hotmail.com',
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.


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.


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'>

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

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.


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>

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

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.


3

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

(rowData, sectionID, rowID, highlightRow) 

Tente fazer isso ...

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

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.


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.


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

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

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?


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

-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.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.