在React中获取关键道具警告,即使设置了关键

时间:2015-08-27 18:03:57

标签: javascript reactjs

问题

我收到了这个警告:

  

警告:数组或迭代器中的每个子节点都应该有一个唯一的“key”prop。检查EventsTable的render方法。有关详细信息,请参阅fb.me/react-warning-keys

     

react-runtime-dev.js?8fefd85d334323f8baa58410bac59b2a7f426ea7:21998警告:数组或迭代器中的每个子节点都应该有一个唯一的“key”prop。检查Event的render方法。有关详细信息,请参阅fb.me/react-warning-keys

来源

这是EventsTable

EventsTable = React.createClass({
  displayName: 'EventsTable',

  render() {
    console.dir(this.props.list);

    return (
      <table className="events-table">
        <thead>
          <tr>
            {_.keys(this.props.list[0]).map(function (key) {
              if (key !== 'attributes') {
                return <th>{key}</th>;
              }
            })}
         </tr>
        </thead>

        <tbody>
          {this.props.list.map(function (row) {
            return (
              <Event key={row.WhatId} data={row} />
            );
          })}
        </tbody>
      </table>
    )
  }
});

这是Event

Event = React.createClass({
  displayName: 'Event',

  render() {
    return (
      <tr>
        {_.keys(this.props.data).map((x) => {
          if (x !== 'attributes')
            return <td>{this.props.data[x]}</td>;
        })}
      </tr>
    )
  }
});

问题

显然我在key组件上有<Event />道具。我遵循惯例,你应该在组件上包含key,而不是HTML本身(换句话说,Event组件中的HTML标签)。根据官方的React文档:

  

密钥应始终直接提供给数组中的组件,而不是数组中每个组件的容器HTML子代:

我非常困惑。为什么我会收到警告?

5 个答案:

答案 0 :(得分:10)

TL;博士

每次渲染列表(使用map)时,都会向列表元素添加唯一的key属性(从{{1}返回的最顶层或&#34; root&#34;元素回调):

map

解释

了解React

中的键

官方Lists and Keys文档显示了您应该如何使用列表,linked reconciliations doc告诉他们这些。

当React重新呈现一个组件时,它会运行一个diff算法,找出新列表和之前版本之间发生了什么变化。比较并不总是微不足道的,但如果每个元素中都有一个唯一的键,则可以清楚地识别出已发生变化的内容。请参阅example in the doc

render() {
  return (
    <div>
      {this.props.data.map( element => {
         // Place the key on to the returned "root" element.
         // Also, in real life this should be a separate component...
         return <div key={element.id}>
           <span>Id (Name): </span>
           <span>{element.id} </span>
           <span>({element.name})</span>
         </div>;
      })}
    </div>
  )
}

很明显,添加了一个带有键<!-- previous --> <ul> <li key="2015">Duke</li> <li key="2016">Villanova</li> </ul> <!-- new --> <ul> <li key="2014">Connecticut</li> <li key="2015">Duke</li> <li key="2016">Villanova</li> </ul> 的新元素,因为我们拥有所有其他键并且没有更改。没有钥匙,这将是模糊不清的。

选择正确的密钥

从现在开始很容易看出:

  • 为什么键应该是唯一的,但仅限于列表中的兄弟姐妹,这一点很重要,因为比较只发生在给定列表的前一个和新元素中。
  • 键对于上一个版本和新版本之间的相同元素应保持相同,否则我们会比较不同的元素,并且无法跟踪更改。这就是为什么建议使用资源的id或(如果它没有)其他一些元素独有的数据,以及为什么你不应该使用像{{1}这样的东西}。

在组件上放置2014属性

您应该将Math.random()属性放置到组件的约定更好的做法,因为当您迭代列表并想要呈现元素时,这清楚地表明您应该将该代码组织到一个单独的组件。

在循环中设置key属性

您从文档中引用的声明:

  

密钥应始终直接提供给数组中的组件,而不是数组中每个组件的容器HTML子代:

表示如果在循环中渲染组件,则应在循环中设置组件的key属性,就像在key组件中一样:

key

错误的方法是将其传递给自己设置EventsTable的组件:

{this.props.list.map(function (row) {
  return (
    <Event key={row.WhatId} data={row} />
  );
})}

这个in this article还有一个很好的例子。

答案 1 :(得分:3)

我也遇到了问题,并在完成link之后修复了它。

像:

{_data.map(function(object, i){
     return <div className={"row"} key={i}> 
           {[ object.name ,
      <b className="fosfo" key={i}> {object.city} </b> , // remove the key
          object.age
      ]}
</div>; 
})}

答案 2 :(得分:1)

我意识到这个线程已经很老了,但是我只是遇到了这个问题,这让我发疯了。当我意识到时,我最终解决了它,因为我有一个<React.Fragment>,它也需要一个唯一的密钥。

答案 3 :(得分:0)

检查是否定义了要传递给key的变量,因为如果它是undefined,则错误将相同,但看起来代码应该可以工作。

答案 4 :(得分:0)

最简单的解决方法是为您要映射的项目创建一个单独的组件,并将密钥添加到该组件。

在现有组件上方创建一个新组件(或通过调用链接到它)。

const TableDataComponent = ({ k }) => {
   return (
     <th>{k}</th>
   )
}

然后在您的代码中使用您的密钥添加该组件:

<tr>
    {arr.map((k) => {
      return <TableDataComponent key={k._id} k={k} />
    })}
</tr>