本文翻译自:Understanding unique keys for array children in React.js
I'm building a React component that accepts a JSON data source and creates a sortable table. 我正在构建一个接受JSON数据源并创建可排序表的React组件。
Each of the dynamic data rows has a unique key assigned to it but I'm still getting an error of: 每个动态数据行都有一个分配给它的唯一键,但是我仍然遇到以下错误:
Each child in an array should have a unique "key" prop. 数组中的每个子代都应具有唯一的“键”道具。
Check the render method of TableComponent. 检查TableComponent的渲染方法。
My TableComponent
render method returns: 我的TableComponent
渲染方法返回:
<table>
<thead key="thead">
<TableHeader columns={columnNames}/>
</thead>
<tbody key="tbody">
{ rows }
</tbody>
</table>
The TableHeader
component is a single row and also has a unique key assigned to it. TableHeader
组件是单行,还为它分配了唯一的键。
Each row
in rows
is built from a component with a unique key: row
中的每一rows
都是由具有唯一键的组件构建的:
<TableRowItem key={item.id} data={item} columns={columnNames}/>
And the TableRowItem
looks like this: 并且TableRowItem
看起来像这样:
var TableRowItem = React.createClass({
render: function() {
var td = function() {
return this.props.columns.map(function(c) {
return <td key={this.props.data[c]}>{this.props.data[c]}</td>;
}, this);
}.bind(this);
return (
<tr>{ td(this.props.item) }</tr>
)
}
});
What is causing the unique key prop error? 是什么导致独特的按键道具错误?
#1楼
参考:https://stackoom.com/question/1urlW/了解React-js中数组子项的唯一键
#2楼
You should add a key to each child as well as each element inside children . 您应该为每个子项以及子项中的每个元素添加一个键。
This way React can handle the minimal DOM change. 这样,React可以处理最小的DOM更改。
In your code, each <TableRowItem key={item.id} data={item} columns={columnNames}/>
is trying to render some children inside them without a key. 在您的代码中,每个<TableRowItem key={item.id} data={item} columns={columnNames}/>
都试图在其中创建一些没有键的子级。
Check this example . 检查此示例 。
Try removing the key={i}
from the <b></b>
element inside the div's (and check the console). 尝试从div的<b></b>
元素中删除key={i}
(并检查控制台)。
In the sample, if we don't give a key to the <b>
element and we want to update only the object.city
, React needs to re-render the whole row vs just the element. 在示例中,如果我们不给钥匙<b>
元素,我们希望只更新object.city
,做出反应需要重新渲染整个行VS只是元素。
Here is the code: 这是代码:
var data = [{name:'Jhon', age:28, city:'HO'},
{name:'Onhj', age:82, city:'HN'},
{name:'Nohj', age:41, city:'IT'}
];
var Hello = React.createClass({
render: function() {
var _data = this.props.info;
console.log(_data);
return(
<div>
{_data.map(function(object, i){
return <div className={"row"} key={i}>
{[ object.name ,
// remove the key
<b className="fosfo" key={i}> {object.city} </b> ,
object.age
]}
</div>;
})}
</div>
);
}
});
React.render(<Hello info={data} />, document.body);
The answer posted by @Chris at the bottom goes into much more detail than this answer. @Chris在底部发布的答案比该答案要详细得多。 Please take a look at https://stackoverflow.com/a/43892905/2325522 请看一下https://stackoverflow.com/a/43892905/2325522
React documentation on the importance of keys in reconciliation: Keys 对文档进行核对: 密钥在对帐中的重要性: 密钥
#3楼
Be careful when iterating over arrays!! 遍历数组时要小心!!
It is a common misconception that using the index of the element in the array is an acceptable way of suppressing the error you are probably familiar with: 一个常见的误解是,使用数组中元素的索引是抑制您可能熟悉的错误的可接受方法:
Each child in an array should have a unique "key" prop.
However, in many cases it is not! 但是,在许多情况下不是! This is anti-pattern that can in some situations lead to unwanted behavior . 这是一种反模式 ,在某些情况下可能导致不良行为 。
Understanding the key
prop 了解key
道具
React uses the key
prop to understand the component-to-DOM Element relation, which is then used for the reconciliation process . React使用key
道具来了解组件与DOM元素的关系,然后将其用于对帐过程 。 It is therefore very important that the key always remains unique , otherwise there is a good chance React will mix up the elements and mutate the incorrect one. 因此,密钥始终保持唯一性非常重要,否则React很有可能会混淆元素并变异不正确的元素。 It is also important that these keys remain static throughout all re-renders in order to maintain best performance. 同样重要的是,这些键在所有重新渲染过程中都应保持静态 ,以保持最佳性能。
That being said, one does not always need to apply the above, provided it is known that the array is completely static. 就是说,只要知道阵列是完全静态的,就不必总是应用上述方法。 However, applying best practices is encouraged whenever possible. 但是,在可能的情况下,鼓励采用最佳实践。
A React developer said in this GitHub issue : 一个React开发人员在GitHub问题中说 :
- key is not really about performance, it's more about identity (which in turn leads to better performance). 关键不在于性能,而在于身份(反过来又可以带来更好的性能)。 randomly assigned and changing values are not identity 随机分配且变化的值不是身份
- We can't realistically provide keys [automatically] without knowing how your data is modeled. 在不知道数据建模方式的情况下,我们无法现实地[自动]提供密钥。 I would suggest maybe using some sort of hashing function if you don't have ids 我建议如果您没有ID,也许使用某种哈希函数
- We already have internal keys when we use arrays, but they are the index in the array. 使用数组时,我们已经具有内部键,但是它们是数组中的索引。 When you insert a new element, those keys are wrong. 当您插入新元素时,这些键是错误的。
In short, a key
should be: 简而言之, key
应该是:
- Unique - A key cannot be identical to that of a sibling component . 唯一 -密钥不能与兄弟组件的密钥相同。
- Static - A key should not ever change between renders. 静态 -关键不应在渲染之间更改。
Using the key
prop 使用key
道具
As per the explanation above, carefully study the following samples and try to implement, when possible, the recommended approach. 根据上面的说明,请仔细研究以下示例,并在可能的情况下尝试实施推荐的方法。
Bad (Potentially) 不好(可能)
<tbody>
{rows.map((row, i) => {
return <ObjectRow key={i} />;
})}
</tbody>
This is arguably the most common mistake seen when iterating over an array in React. 可以说这是在React中遍历数组时最常见的错误。 This approach isn't technically "wrong" , it's just... "dangerous" if you don't know what you are doing. 从技术上讲,这种方法不是“错误”的 ,只是如果您不知道自己在做什么,则是“危险” 。 If you are iterating through a static array then this is a perfectly valid approach (eg an array of links in your navigation menu). 如果要遍历静态数组,则这是一种完全有效的方法(例如,导航菜单中的链接数组)。 However, if you are adding, removing, reordering or filtering items, then you need to be careful. 但是,如果要添加,删除,重新排序或过滤项目,则需要小心。 Take a look at this detailed explanation in the official documentation. 请看官方文档中的详细说明 。
class MyApp extends React.Component { constructor() { super(); this.state = { arr: ["Item 1"] } } click = () => { this.setState({ arr: ['Item ' + (this.state.arr.length+1)].concat(this.state.arr), }); } render() { return( <div> <button onClick={this.click}>Add</button> <ul> {this.state.arr.map( (item, i) => <Item key={i} text={"Item " + i}>{item + " "}</Item> )} </ul> </div> ); } } const Item = (props) => { return ( <li> <label>{props.children}</label> <input value={props.text} /> </li> ); } ReactDOM.render(<MyApp />, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script> <div id="app"></div>
In this snippet we are using a non-static array and we are not restricting ourselves to using it as a stack. 在此代码段中,我们使用的是非静态数组,我们并不仅限于将其用作堆栈。 This is an unsafe approach (you'll see why). 这是一种不安全的方法(您会明白为什么)。 Note how as we add items to the beginning of the array (basically unshift), the value for each <input>
remains in place. 请注意,当我们将项目添加到数组的开头(基本上未移位)时,每个<input>
的值都保持不变。 Why? 为什么? Because the key
doesn't uniquely identify each item. 因为key
不能唯一地标识每个项目。
In other words, at first Item 1
has key={0}
. 换句话说,第一Item 1
首先具有key={0}
。 When we add the second item, the top item becomes Item 2
, followed by Item 1
as the second item. 当我们添加第二个项目时,最上面的项目变成Item 2
,然后是Item 1
作为第二个项目。 However, now Item 1
has key={1}
and not key={0}
anymore. 但是,现在Item 1
具有key={1}
而不是key={0}
。 Instead, Item 2
now has key={0}
!! 相反, Item 2
现在具有key={0}
!
As such, React thinks the <input>
elements have not changed, because the Item
with key 0
is always at the top! 因此,React认为<input>
元素没有改变,因为键0
的Item
始终位于顶部!
So why is this approach only sometimes bad? 那么,为什么这种方法有时只是不好的呢?
This approach is only risky if the array is somehow filtered, rearranged, or items are added/removed. 仅当以某种方式过滤,重新排列或添加/删除项目时,此方法才有风险。 If it is always static, then it's perfectly safe to use. 如果它始终是静态的,则使用起来绝对安全。 For example, a navigation menu like ["Home", "Products", "Contact us"]
can safely be iterated through with this method because you'll probably never add new links or rearrange them. 例如,可以安全地迭代使用["Home", "Products", "Contact us"]
类的导航菜单["Home", "Products", "Contact us"]
因为您可能永远不会添加新链接或重新排列它们。
In short, here's when you can safely use the index as key
: 简而言之,这是您可以安全地将索引用作key
:
- The array is static and will never change. 该数组是静态的,永远不会改变。
- The array is never filtered (display a subset of the array). 永远不会过滤数组(显示数组的子集)。
- The array is never reordered. 阵列从不重新排序。
- The array is used as a stack or LIFO (last in, first out). 该阵列用作堆栈或LIFO(后进先出)。 In other words, adding can only be done at the end of the array (ie push), and only the last item can ever be removed (ie pop). 换句话说,添加只能在数组的末尾(即推入)完成,并且只能删除最后一项(即弹出)。
Had we instead, in the snippet above, pushed the added item to the end of the array, the order for each existing item would always be correct. 相反,如果我们在上面的代码段中将添加的项目推到数组的末尾,则每个现有项目的顺序将始终是正确的。
Very bad 很坏
<tbody>
{rows.map((row) => {
return <ObjectRow key={Math.random()} />;
})}
</tbody>
While this approach will probably guarantee uniqueness of the keys, it will always force react to re-render each item in the list, even when this is not required. 尽管此方法可能会保证键的唯一性,但即使不需要时,也会始终强制做出反应以重新呈现列表中的每个项目。 This a very bad solution as it greatly impacts performance. 这是一个非常糟糕的解决方案,因为它会极大地影响性能。 Not to mention that one cannot exclude the possibility of a key collision in the event that Math.random()
produces the same number twice. 更不用说在Math.random()
两次产生相同数字的情况下,不能排除发生键冲突的可能性。
Unstable keys (like those produced by
Math.random()
) will cause many component instances and DOM nodes to be unnecessarily recreated, which can cause performance degradation and lost state in child components. 不稳定的键(如Math.random()
产生的键)将导致不必要地重新创建许多组件实例和DOM节点,这可能导致性能下降和子组件中的状态丢失。
Very good 很好
<tbody>
{rows.map((row) => {
return <ObjectRow key={row.uniqueId} />;
})}
</tbody>
This is arguably the best approach because it uses a property that is unique for each item in the dataset. 可以说这是最好的方法,因为它使用的属性对于数据集中的每个项目都是唯一的。 For example, if rows
contains data fetched from a database, one could use the table's Primary Key ( which typically is an auto-incrementing number ). 例如,如果rows
包含从数据库中获取的数据,则可以使用表的主键( 通常是自动递增的数字 )。
The best way to pick a key is to use a string that uniquely identifies a list item among its siblings. 选择键的最佳方法是使用一个字符串,该字符串唯一地标识其同级项中的列表项。 Most often you would use IDs from your data as keys 通常,您会使用数据中的ID作为键
Good 好
componentWillMount() {
let rows = this.props.rows.map(item => {
return {uid: SomeLibrary.generateUniqueID(), value: item};
});
}
...
<tbody>
{rows.map((row) => {
return <ObjectRow key={row.uid} />;
})}
</tbody>
This is also a good approach. 这也是一个好方法。 If your dataset does not contain any data that guarantees uniqueness ( eg an array of arbitrary numbers ), there is a chance of a key collision. 如果您的数据集不包含任何保证唯一性的数据( 例如,任意数字的数组 ),则可能会发生键冲突。 In such cases, it is best to manually generate a unique identifier for each item in the dataset before iterating over it. 在这种情况下,最好在迭代之前为数据集中的每个项目手动生成唯一的标识符。 Preferably when mounting the component or when the dataset is received ( eg from props
or from an async API call ), in order to do this only once , and not each time the component re-renders. 最好在安装组件时或在接收到数据集时( 例如从props
或从异步API调用接收到),以便仅执行一次 ,而不是每次组件都重新渲染。 There are already a handful of libraries out there that can provide you such keys. 已经有少数可以为您提供此类密钥的库。 Here is one example: react-key-index . 这是一个示例: react-key-index 。
#4楼
Warning: Each child in an array or iterator should have a unique "key" prop. 警告:数组或迭代器中的每个子代都应具有唯一的“键”道具。
This is a warning as for array items which we are going to iterate over will need a unique resemblance. 这是一个警告,因为要迭代的数组项将需要独特的相似之处。
React handles iterating component rendering as arrays. React将迭代的组件渲染处理为数组。
Better way to resolve this is provide index on the array items you are going to iterate over.for example: 解决此问题的更好方法是在要迭代的数组项上提供索引,例如:
class UsersState extends Component
{
state = {
users: [
{name:"shashank", age:20},
{name:"vardan", age:30},
{name:"somya", age:40}
]
}
render()
{
return(
<div>
{
this.state.users.map((user, index)=>{
return <UserState key={index} age={user.age}>{user.name}</UserState>
})
}
</div>
)
}
index is React built-in props. 索引是React内置的道具。
#5楼
Just add the unique key to the your Components 只需将唯一键添加到您的组件中
data.map((marker)=>{
return(
<YourComponents
key={data.id} // <----- unique key
/>
);
})
#6楼
I fixed this using Guid for each key like this: Generating Guid: 我为每个键使用Guid修复了此问题,如下所示:
guid() {
return this.s4() + this.s4() + '-' + this.s4() + '-' + this.s4() + '-' +
this.s4() + '-' + this.s4() + this.s4() + this.s4();
}
s4() {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
}
And then assigning this value to markers: 然后将此值分配给标记:
{this.state.markers.map(marker => (
<MapView.Marker
key={this.guid()}
coordinate={marker.coordinates}
title={marker.title}
/>
))}