Javascript 了解 React.js 中数组子项的唯一键
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/28329382/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Understanding unique keys for array children in React.js
提问by Brett DeWoody
I'm building a React component that accepts a JSON data source and creates a sortable table.
Each of the dynamic data rows has a unique key assigned to it but I'm still getting an error of:
我正在构建一个接受 JSON 数据源并创建一个可排序表的 React 组件。
每个动态数据行都有一个分配给它的唯一键,但我仍然收到以下错误:
Each child in an array should have a unique "key" prop.
Check the render method of TableComponent.
数组中的每个孩子都应该有一个唯一的“key”道具。
检查TableComponent 的render 方法。
My TableComponentrender method returns:
我的TableComponent渲染方法返回:
<table>
<thead key="thead">
<TableHeader columns={columnNames}/>
</thead>
<tbody key="tbody">
{ rows }
</tbody>
</table>
The TableHeadercomponent is a single row and also has a unique key assigned to it.
该TableHeader组件是单行,并且还分配有唯一键。
Each rowin rowsis built from a component with a unique key:
每个rowinrows都是由一个具有唯一键的组件构建的:
<TableRowItem key={item.id} data={item} columns={columnNames}/>
And the TableRowItemlooks 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?
是什么导致了唯一键道具错误?
采纳答案by jmingov
You should add a key to each child as well as each element inside children.
您应该为每个子项以及 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).
尝试key={i}从<b></b>div 内的元素中删除。(并检查控制台)。
In the sample, if we don't give a key to the <b>element and we want to update onlythe 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 @Chrisat the bottom goes into much more detail than this answer. Please take a look at https://stackoverflow.com/a/43892905/2325522
@Chris在底部发布的答案比这个答案更详细。请看https://stackoverflow.com/a/43892905/2325522
React documentation on the importance of keys in reconciliation: Keys
关于密钥在和解中的重要性的 React 文档:Keys
回答by Chris
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-patternthat can in somesituations lead to unwanted behavior.
然而,在许多情况下并非如此!这是一种反模式,在某些情况下会导致不需要的行为。
Understanding the keyprop
理解key道具
React uses the keyprop to understand the component-to-DOM Element relation, which is then used for the reconciliation process. It is therefore very important that the key alwaysremains unique, otherwise there is a good chance React will mix up the elements and mutate the incorrect one. It is also important that these keys remain staticthroughout all re-renders in order to maintain best performance.
React 使用keyprop 来理解组件到 DOM 元素的关系,然后用于协调过程。因此,键始终保持唯一性非常重要,否则 React 很可能会混淆元素并改变不正确的元素。为了保持最佳性能,这些键在所有重新渲染过程中保持静态也很重要。
That being said, one does not alwaysneed to apply the above, provided it is knownthat 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
- 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.
- 关键不是真正的性能,它更多的是关于身份(这反过来会导致更好的性能)。随机分配和变化的值不是身份
- 在不知道您的数据是如何建模的情况下,我们实际上无法[自动] 提供密钥。如果您没有 id,我建议您使用某种散列函数
- 当我们使用数组时,我们已经有了内部键,但它们是数组中的索引。当您插入一个新元素时,这些键是错误的。
In short, a keyshould be:
简而言之,akey应该是:
- Unique- A key cannot be identical to that of a sibling component.
- Static- A key should not ever change between renders.
- 唯一- 键不能与同级组件的键相同。
- 静态- 渲染之间不应更改键。
Using the keyprop
使用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. 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 (e.g. 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 explanationin the official documentation.
这可以说是在 React 中迭代数组时最常见的错误。这种方法在技术上并没有“错误”,它只是……如果您不知道自己在做什么,则是“危险的”。如果您正在遍历静态数组,那么这是一种完全有效的方法(例如,导航菜单中的链接数组)。但是,如果您要添加、删除、重新排序或过滤项目,则需要小心。看看官方文档中的这个详细解释。
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. Why? Because the keydoesn't uniquely identify each item.
在这个片段中,我们使用了一个非静态数组,我们不限制自己将它用作堆栈。这是一种不安全的方法(您会明白为什么)。请注意,当我们将项添加到数组的开头(基本上是 unshift)时,每个项的值<input>保持不变。为什么?因为key不唯一标识每个项目。
In other words, at first Item 1has key={0}. When we add the second item, the top item becomes Item 2, followed by Item 1as the second item. However, now Item 1has key={1}and not key={0}anymore. Instead, Item 2now has key={0}!!
换句话说,起初Item 1有key={0}。当我们添加第二个项目时,最上面的项目变成Item 2,然后Item 1是第二个项目。然而,现在Item 1有key={1},key={0}不再有。相反,Item 2现在有key={0}!!
As such, React thinks the <input>elements have not changed, because the Itemwith key 0is always at the top!
因此,React 认为<input>元素没有改变,因为Itemwith 键0总是在顶部!
So why is this approach only sometimesbad?
那么为什么这种方法只是有时不好呢?
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"]可以使用此方法安全地迭代导航菜单,因为您可能永远不会添加新链接或重新排列它们。
In short, here's when you can safelyuse 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). In other words, adding can only be done at the end of the array (i.e push), and only the last item can ever be removed (i.e pop).
- 数组是静态的,永远不会改变。
- 从不过滤数组(显示数组的子集)。
- 数组永远不会重新排序。
- 该数组用作堆栈或 LIFO(后进先出)。换句话说,添加只能在数组的末尾进行(即推送),并且只能删除最后一项(即弹出)。
Had we instead, in the snippet above, pushedthe 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 alwaysforce 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.
虽然这种方法可能会保证键的唯一性,但它总是会强制 react 重新渲染列表中的每个项目,即使这不是必需的。这是一个非常糟糕的解决方案,因为它极大地影响了性能。更不用说在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 approachbecause it uses a property that is unique for each item in the dataset. For example, if rowscontains 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 (e.g. 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 (e.g. from propsor from an async API call), in order to do this only once, and not each time the component re-renders. There are already a handful of libraries out there that can provide you such keys. Here is one example: react-key-index.
这也是一个很好的方法。如果您的数据集不包含任何保证唯一性的数据(例如任意数字的数组),则可能会发生密钥冲突。在这种情况下,最好在迭代之前为数据集中的每个项目手动生成一个唯一标识符。最好在安装组件或接收数据集时(例如,来自props或来自异步 API 调用),以便仅执行一次,而不是每次重新渲染组件时执行此操作。已经有一些库可以为您提供这样的密钥。这是一个示例:react-key-index。
回答by apil.tamang
This may or not help someone, but it might be a quick reference. This is also similar to all the answers presented above.
这可能对某人有帮助,也可能没有帮助,但它可能是一个快速参考。这也类似于上面提供的所有答案。
I have a lot of locations that generate list using the structure below:
我有很多位置使用以下结构生成列表:
return (
{myList.map(item => (
<>
<div class="some class">
{item.someProperty}
....
</div>
</>
)}
)
After a little trial and error (and some frustrations), adding a key property to the outermost block resolved it. Also note that the <> tag is now replaced with the tag now.
经过一些试验和错误(以及一些挫折),向最外层块添加一个关键属性解决了它。另请注意,<> 标记现在已替换为标记。
return (
{myList.map((item, index) => (
<div key={index}>
<div class="some class">
{item.someProperty}
....
</div>
</div>
)}
)
Of course, I've been naively using the iterating index (index) to populate the key value in the above example. Ideally, you'd use something which is unique to the list item.
当然,在上面的例子中,我一直很天真地使用迭代索引(index)来填充键值。理想情况下,您将使用列表项独有的内容。
回答by Shashank Malviya
Warning: Each child in an array or iterator should have a unique "key" prop.
警告:数组或迭代器中的每个子元素都应该有一个唯一的“key”属性。
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.
index 是 React 内置的 props。
回答by Bashirpour
Just add the unique keyto the your Components
只需将唯一键添加到您的组件中
data.map((marker)=>{
return(
<YourComponents
key={data.id} // <----- unique key
/>
);
})
回答by Gerd
Check: key = undef !!!
检查:key = undef !!!
You got also the warn message:
您还收到警告消息:
Each child in a list should have a unique "key" prop.
if your code is complete right, but if on
如果您的代码完整正确,但如果打开
<ObjectRow key={someValue} />
someValue is undefined!!! Please check this first. You can save hours.
someValue 未定义!!!请先检查这个。您可以节省数小时。
回答by Khadim Rana
Best solution of define unique key in react: inside the map you initialized the name post then key define by key={post.id} or in my code you see i define the name item then i define key by key={item.id}:
在反应中定义唯一键的最佳解决方案:在地图内初始化名称 post 然后键定义 key={post.id} 或者在我的代码中你看到我定义名称项然后我定义键 key={item.id }:
<div className="container">
{posts.map(item =>(
<div className="card border-primary mb-3" key={item.id}>
<div className="card-header">{item.name}</div>
<div className="card-body" >
<h4 className="card-title">{item.username}</h4>
<p className="card-text">{item.email}</p>
</div>
</div>
))}
</div>
回答by prime
This is a warning, But addressing this will make Reacts rendering much FASTER,
这是一个警告,但解决这个问题将使 Reacts 渲染得更快,
This is because Reactneeds to uniquely identify each items in the list. Lets say if the state of an element of that list changes in Reacts Virtual DOMthen React needs to figure out which element got changed and where in the DOM it needs to change so that browser DOM will be in sync with the Reacts Virtual DOM.
这是因为React需要唯一标识列表中的每个项目。假设如果该列表的元素的状态在 Reacts 中发生变化,Virtual DOM那么 React 需要确定哪个元素发生了变化以及它需要在 DOM 中的哪个位置更改,以便浏览器 DOM 与 Reacts 虚拟 DOM 同步。
As a solution just introduce a keyattribute to each litag. This keyshould be a unique value to each element.
作为解决方案,只需key为每个li标签引入一个属性。这key应该是每个元素的唯一值。
回答by Ramesh
var TableRowItem = React.createClass({
render: function() {
var td = function() {
return this.props.columns.map(function(c, i) {
return <td key={i}>{this.props.data[c]}</td>;
}, this);
}.bind(this);
return (
<tr>{ td(this.props.item) }</tr>
)
}
});
This will sove the problem.
这将解决问题。
回答by Felipe
I was running into this error message because of <></>being returned for some items in the array when instead nullneeds to be returned.
我<></>遇到此错误消息是因为在需要返回数组中的某些项目时null返回。

