了解React.js中数组子项的唯一键

我正在构建一个React组件,它接受JSON数据源并创建一个可排序的表。
每个动态数据行都有一个分配给它的唯一键,但我仍然收到错误:

数组中的每个子元素都应该有一个唯一的“key”道具。
检查TableComponent的渲染方法。

我的TableComponent渲染方法返回:

<table><thead key="thead"><TableHeader columns={columnNames}/></thead><tbody key="tbody">{ rows }</tbody></table>

TableHeader组件是单行,也有一个唯一的键分配给它。

rows中的每个row都是由具有唯一键的组件构建的:

<TableRowItem key={item.id} data={item} columns={columnNames}/>

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

是什么导致了唯一的关键道具错误?

1119272 次浏览

您应该为每个孩子添加一个键以及子元素内部的每个元素

通过这种方式,React可以处理最小的DOM更改。

在您的代码中,每个<TableRowItem key={item.id} data={item} columns={columnNames}/>都试图在没有键的情况下呈现其中的一些子级。

检查这个例子

尝试从div的<b></b>元素中删除key={i}(并检查控制台)。

在示例中,如果我们没有为<b>元素提供键,并且我们想将只有更新为object.city,React需要重新渲染整行而不仅仅是元素。

以下是代码:

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

@Chris在底部发布的答案比这个答案更详细。

对键在和解中的重要性做出反应留档:钥匙

迭代数组时要小心!!

一个常见的误解是,使用数组中元素的索引是抑制您可能熟悉的错误的一种可接受的方法:

Each child in an array should have a unique "key" prop.

然而,在许多情况下它不是!这是反模式,可以在一些情况下导致不受欢迎的行为


了解key道具

React使用key道具来理解组件到DOM元素的关系,然后将其用于和解进程。因此,键总是保持独特非常重要,否则React很有可能会混淆元素并改变不正确的元素。为了保持最佳性能,这些键保持不变在所有重新渲染中也很重要。

话虽如此,只要数组是完全静态的,就不需要总是应用上述内容。但是,鼓励尽可能应用最佳实践。

一位React开发者在这个github问题中说:

  • key与性能无关,它更多的是关于身份(这反过来会带来更好的性能)。随机分配和更改值不是身份
  • 如果不知道您的数据是如何建模的,我们就无法实际地[自动]提供密钥。如果您没有ID,我建议您使用某种哈希函数
  • 当我们使用数组时,我们已经有了内部键,但它们是数组中的索引。当你插入一个新元素时,这些键是错误的。

简而言之,key应该是:

  • 独特-密钥不能与兄弟成分相同。
  • 静态-一个键不应该在渲染之间改变。

使用key道具

根据上面的解释,仔细研究以下示例,并在可能的情况下尝试实施推荐的方法。


坏(潜在)

<tbody>{rows.map((row, i) => {return <ObjectRow key={i} />;})}</tbody>

这可以说是在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>

在这个代码片段中,我们使用了一个非静态数组,并且我们没有限制自己将其用作堆栈。这是一种不安全的方法(你会明白为什么)。请注意,当我们将项目添加到数组的开头时(基本上是取消移位),每个<input>的值保持不变。为什么?因为key并不唯一地标识每个项目。

换句话说,最初Item 1key={0}。当我们添加第二个项目时,顶部的项目变成了Item 2,然后是Item 1作为第二个项目。然而,现在Item 1key={1},不再是key={0}。相反,Item 2现在有key={0}!!

因此,React认为<input>元素没有改变,因为Item和键0总是在顶部!

那么为什么这种方法只是有时不好呢?

这种方法只有在数组被以某种方式过滤、重新排列或添加/删除项目时才有风险。如果它总是静态的,那么使用它是完全安全的。例如,像["Home", "Products", "Contact us"]这样的导航菜单可以安全地使用这种方法迭代,因为你可能永远不会添加新链接或重新排列它们。

简而言之,这是你可以安全使用索引作为key的时候:

  • 数组是静态的,永远不会改变。
  • 永远不会过滤数组(显示数组的子集)。
  • 数组永远不会重新排序。
  • 数组用作堆栈或LIFO(后进先出)。换句话说,添加只能在数组的末尾完成(即推送),只能删除最后一项(即弹出)。

如果我们在上面的代码片段中,将添加的项目添加到数组的末尾,则每个现有项目的顺序将始终是正确的。


非常糟糕

<tbody>{rows.map((row) => {return <ObjectRow key={Math.random()} />;})}</tbody>

虽然这种方法可能会保证键的唯一性,但即使不需要,它也会强制反应重新渲染列表中的每个项目。这是一个非常糟糕的解决方案,因为它极大地影响了性能。更不用说,如果Math.random()产生两次相同的数字,也不能排除键碰撞的可能性。

不稳定的键(如Math.random()生成的键)将导致不必要地重新创建许多组件实例和DOM节点,这可能导致子组件的性能下降和状态丢失。


非常好

<tbody>{rows.map((row) => {return <ObjectRow key={row.uniqueId} />;})}</tbody>

这可以说是最佳方法,因为它使用的属性对数据集中的每个项目都是唯一的。例如,如果rows包含从数据库获取的数据,则可以使用表的主键(这通常是一个自动递增的数字)。

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


componentWillMount() {let rows = this.props.rows.map(item => {return {uid: SomeLibrary.generateUniqueID(), value: item};});}
...
<tbody>{rows.map((row) => {return <ObjectRow key={row.uid} />;})}</tbody>

这也是一个好方法。如果你的数据集不包含任何保证唯一性的数据(例如,任意数字的数组),就有可能发生键碰撞。在这种情况下,最好在迭代数据集中的每个项目之前手动为其生成一个唯一标识符。最好是在挂载组件时或收到数据集时(例如,从#0或从异步API调用),这样做只有一次,而不是每次组件重新渲染时。已经有一些库可以为你提供这样的键。这是一个例子:反应键

警告:数组或迭代器中的每个子元素都应该有一个唯一的“key”prop。

这是一个警告,因为我们将要迭代的数组项需要独特的相似度。

React将迭代组件渲染处理为数组。

解决这个问题的更好方法是在你要迭代的数组项上提供索引over.for例子:

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是React内置的道具。

只需将唯一密钥添加到您的组件

data.map((marker)=>{return(<YourComponentskey={data.id}     // <----- unique key/>);})

这是一个警告,但是解决这个问题会让React渲染更快

这是因为React需要唯一标识列表中的每个项目。假设该列表中的元素的状态在ReactVirtual DOM中发生变化,那么React需要找出哪个元素发生了变化以及它需要在DOM中的哪个位置进行更改,以便浏览器DOM与React Virtual DOM同步。

作为解决方案,只需为每个li标签引入key属性。这个key应该是每个元素的唯一值。

检查:key=undef!!!

您还收到警告消息:

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

如果你的代码是完全正确的,但如果

<ObjectRow key={someValue} />

一些值未定义!!!请先检查这个。您可以节省时间。

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

这将解决这个问题。

在React中定义唯一密钥的最佳解决方案:在map中,你初始化了name post,然后通过key={post.id}定义key,或者在我的代码中,你看到我定义了name项目,然后我通过key={item.id}定义key:

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

这可能对某人有帮助,也可能没有帮助,但这可能是一个快速的参考。这也类似于上面给出的所有答案。

我有很多位置使用下面的结构生成列表:

return ({myList.map(item => (<><div class="some class">{item.someProperty}....</div></>)})         

经过一些尝试和错误(以及一些挫折),向最外层块添加key属性解决了它。另外,请注意,<>标签现在替换为<div>标签。

return (  
{myList.map((item, index) => (<div key={index}><div class="some class">{item.someProperty}....</div></div>)})

当然,在上面的例子中,我一直天真地使用迭代索引(index)来填充键值。理想情况下,您应该使用列表项唯一的东西。

我遇到了这个错误消息,因为当需要返回null时,数组中的某些项目返回了<></>

在ReactJS中,如果你正在渲染一个元素数组,你应该为每个元素都有一个唯一的键。通常这种情况是创建一个列表。

示例:

function List() {const numbers = [0,1,2,3]; 
return (<ul>{numbers.map((n) => <li> {n} </li>)}</ul>);}
ReactDOM.render(<List />,document.getElementById('root'));

在上面的示例中,它使用li标签创建动态列表,因此由于li标签没有唯一键,它会显示错误。

固定后:

function List() {const numbers = [0,1,2,3]; 
return (<ul>{numbers.map((n) => <li key={n}> {n} </li>)}</ul>);}
ReactDOM.render(<List />,document.getElementById('root'));

当您没有唯一密钥时使用map的替代解决方案(Reacteslint不推荐这样做):

function List() {const numbers = [0,1,2,3,4,4]; 
return (<ul>{numbers.map((n,i) => <li key={i}> {n} </li>)}</ul>);}
ReactDOM.render(<List />,document.getElementById('root'));

直播示例:https://codepen.io/spmsupun/pen/wvWdGwG

我有一个唯一的钥匙,只需要像这样通过它作为道具:

<CompName key={msg._id} message={msg} />

这个页面很有帮助:

https://reactjs.org/docs/lists-and-keys.html#keys

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

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

请参阅列表和键-反应

在我的例子中,将id设置为标签

<tbody key={i}>

问题解决了。

If you are getting error like :
> index.js:1 Warning: Each child in a list should have a unique "key" prop.
Check the render method of `Home`. See https://reactjs.org/link/warning-keys for more information.
Then Use inside map function like:
{classes.map((user, index) => (<Card  **key={user.id}**></Card>))}`enter code here`

您应该为tbody的每个子键使用唯一值,其中

  • 该值不能为其兄弟姐妹的相同(相同)
  • 不应该在渲染之间改变

例如,键值可以是数据库标识UUID(通用唯一标识符)

这里的键是手动处理的:

<tbody>{rows.map((row) => <ObjectRow key={row.uuid} />)}</tbody>

您还可以让React使用React.Children.to阵列处理密钥

<tbody>{React.Children.toArray(rows.map((row) => <ObjectRow />))}</tbody>

我遇到了一个类似的问题,但不准确。尝试了所有可能的解决方案,无法摆脱该错误

数组中的每个子元素都应该有一个唯一的“key”prop。

然后我试着在另一个本地主机上打开它。我不知道怎么做,但它奏效了!

这是一个简单的例子,我使用了一个反应条件,首先是&&,然后是map,在我添加了key的用户ID以确保它是唯一的

 <tbody>{users &&users.map((user) => {return <tr key={user._id}><td>{user.username}</td><td><inputname="isGoing"type="checkbox"checked={user.isEnabled}onChange={handleInputChangeNew}/></td><td>{user.role.roleTitle} - {user.role.department.departmentName}</td>{/*<td className="text-right"><Button>ACTION</Button></td>*/}</tr>})}

</tbody>

我不同意详细的解释,但这个答案的关键是“关键”只需将key属性放在标记中,并确保每次迭代时都为其提供唯一的值

#确保key的值不与其他值冲突

示例

<div>{conversation.map(item => (<div key={item.id  } id={item.id}></div>))}</div>

其中会话是一个数组,如下所示:

  const conversation = [{id:"unique"+0,label:"OPEN"},{id:"unique"+1,label:"RESOLVED"},{id:"unique"+2,label:"ARCHIVED"},]

如果您正在努力解决此错误列表中的每个孩子都应该有一个唯一的“钥匙”道具。

通过在呈现元素内为key属性声明索引值来解决。

App.js部分

import Map1 from './Map1';
const arr = [1,2,3,4,5];
const App = () => {return (<>     
<Map1 numb={arr} />
</>)}
export default App

Map.js部分

const Map1 = (props) => {
let itemTwo = props.numb;let itemlist = itemTwo.map((item,index) => <li key={index}>{item}</li>)
return (<><ul><li style={liStyle}>{itemlist}</li></ul></>)}
export default Map1

你的密钥应该unique.like一个唯一的ID,你的代码应该是这样的

<div>{products.map(product => (<Product key={product.id}></Product>))}</div>

如果我们有数组对象数据。然后我们映射以显示数据。并传递唯一ID(key={product.id}),因为浏览器可以选择唯一的数据。

example : [{"id": "1","name": "walton glass door","suplier": "walton group","price": "50000","quantity": "25","description":"Walton Refrigerator is the Best Refrigerator brand in bvBangladesh "},{        
"id": "2","name": "walton glass door","suplier": "walton group","price": "40000","quantity": "5","description":"Walton Refrigerator is the Best Refrigerator brand inBangladesh "},}

现在我们正在映射数据并传递唯一的id:

{products.map(product => <product product={product} key={product.id}</product>)}

下面是React文档,它们很好地解释了使用Key属性,key应该在父组件中定义,它不应该在子组件中使用。React Docs

在此处输入图片描述

在此处输入图片描述

直观的解释。

  1. 不正确的方式key=index(数组的)

输入图片描述

如您所见,标签3、标签2和标签1所有重新呈现(在Elements面板中闪烁)。

  1. 正确的方法key=uniqueId输入图片描述

只有顶部新元素闪烁(重新渲染)。