警告:数组或迭代器中的每个子节点都应该具有唯一的“键”支柱。检查`ListView`的render方法

时间:2016-01-03 12:01:47

标签: facebook listview reactjs react-native

我使用<{3}}为iOS和Android构建了一个带 ReactNative 的应用。使用有效数据源填充列表视图时,屏幕底部会显示以下警告:

  

警告:数组或迭代器中的每个子节点都应该有一个唯一的“键”   支柱。检查ListView的呈现方法。

此警告的目的是什么?在他们链接到ListView之后的消息之后,讨论完全不同的事情与本机反应无关,但与基于网络的反应有关。

我的ListView是使用这些语句构建的:

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

    );
}

我的DataSource包含以下内容:

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

ListView-Rows使用以下内容进行渲染:

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

一切正常并且如预期的那样,除了警告似乎对我来说完全无稽之谈。

将key属性添加到我的“DetailItem”-Class并没有解决问题。

这就是由于“cloneWithRows”而真正传递给ListView的内容:

_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: '' } ] },

如一键看,每条记录都有一个关键属性。警告仍然存在。

19 个答案:

答案 0 :(得分:71)

我现在已经和你一样完全同样的问题了,在看了上面的一些建议之后,我终于解决了这个问题。

事实证明(至少对我而言),我需要为我从renderSeparator方法返回的组件提供一个键(一个名为'key'的道具)。向renderRow或renderSectionHeader添加一个键没有做任何事情,但是将它添加到renderSeparator会使警告消失。

希望有所帮助。

答案 1 :(得分:59)

您需要提供密钥

如果您有一个关键属性,请尝试在ListView Rows中执行此操作:

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

如果没有,请尝试将该项目添加为关键字:

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

答案 2 :(得分:29)

您还可以将迭代计数(i)用作key

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

答案 3 :(得分:17)

更改您的代码:

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

致:

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

然后解决了。

答案 4 :(得分:6)

在列表的呈现根组件中添加道具“键”。

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

答案 5 :(得分:5)

当您未向列表项添加密钥时会出现此警告。按照反应js文档 -

  

键帮助React识别哪些项目已更改,已添加或已添加   除去。键应该给予数组内的元素   元素稳定的身份:

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

选择密钥的最佳方法是使用唯一标识的字符串   兄弟姐妹中的一个列表项。大多数情况下,你会使用你的ID   数据作为键:

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

当您没有渲染项目的稳定ID时,您可以使用   项目索引作为关键作为最后的手段

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

答案 6 :(得分:4)

我通过向renderSeparator组件添加属性来修复它,代码在这里:

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

此警告的关键字是“唯一”,sectionID + rowID在ListView中返回唯一值。

答案 7 :(得分:3)

我用来解决这个问题的具体代码是:

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

我包含特定代码,因为您需要键是唯一的 - 即使对于分隔符也是如此。如果你做了类似的事情,例如,如果你把它设置为常量,你将会得到另一个关于重用密钥的恼人错误。如果您不了解JSX,那么构建JS回调以执行各个部分可能会非常痛苦。

在ListView上,显然附上了这个:

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

感谢coldbuffet和Nader Dabit指出了这条道路。

答案 8 :(得分:3)

假设renderDetailItem方法具有following signature ...

(rowData, sectionID, rowID, highlightRow) 

尝试这样做......

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

答案 9 :(得分:1)

检查:key = undef !!!

您还会收到警告消息:

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

如果您的代码是完整的,但正确的话,

<MyComponent key={someValue} />

someValue未定义!!!请先检查一下。您可以节省几个小时。

答案 10 :(得分:1)

当您使用任何循环函数并在没有键的情况下渲染某些 HTML 元素时,即使您的父 div 有键,也会出现此错误,并且要解决此问题,您必须传递键。 >

请查看以下屏幕截图以更好地理解:

check the selected key and added in for each loop

我已按照上述方式修复了此警告。

答案 11 :(得分:1)

如果你有一个空标签 <> 作为循环内结构的顶层,你会得到同样的错误:

return <select>
    {Object.values(countries).map(c => {
        return (<>           // <== EMPTY TAG!
            <option value={c.id}>{c.name}</option>
            <States countryId={c.id} />
        </>)
    }
</select>

您可以使用 <React.Fragment> 的完整语法而不是短 <> 并将您的密钥添加到完整标签中:

import {Fragment} from 'react';

return <select>
    {Object.values(countries).map(c => {
        return (<Fragment key={c.id}> // You can also use <React.Fragment> without import
            <option value={c.id}>{c.name}</option>
            <States countryId={c.id} />
        </Fragment>)
    }
</select>

答案 12 :(得分:0)

似乎两个条件都得到满足,或许关键('联系')是问题

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

答案 13 :(得分:0)

这不能足够强调:

键仅在周围数组的上下文中有意义

“例如,如果提取ListItem组件,则应将键保留在数组的元素上,而不要保留在ListItem本身的

  • 元素上。 -https://reactjs.org/docs/lists-and-keys.html#extracting-components-with-keys

  • 答案 14 :(得分:0)

    这里是基于我的理解。希望它会有所帮助。作为后面的示例,它应该呈现所有组件的列表。每个组件的根标记都必须具有key。它不必是唯一的。不能为key=0key='0'等。它看起来像键是没用的。

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

    答案 15 :(得分:0)

    让我着迷这个问题的是,我认为需要将密钥应用于看起来像“真实”或DOM HTML元素,而不是我定义的JSX元素。

    当然,在React中,我们正在使用虚拟DOM,因此我们定义的<MyElement>的React JSX元素与看起来像<div>这样的真实DOM HTML元素的元素一样重要。 / p>

    这有意义吗?

    答案 16 :(得分:0)

    就我而言,我使用的是语义UI React“卡”视图。将密钥添加到所构造的每张卡后,警告就会消失,例如:

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

    答案 17 :(得分:0)

    这对我有用。

    <View>
        {
            array.map((element, index) => {
                return(
                    <React.Fragment key= {`arrayElement${index}`}>
                        {element}
                    </React.Fragment>
                );
            })
        }
    </View>
    

    答案 18 :(得分:-1)

    如果您在React应用程序中使用<Fade in>元素,请同时在其中添加key={}属性,否则控制台中将出现错误。