提问人:Brett DeWoody 提问时间:2/5/2015 最后编辑:Brett DeWoody 更新时间:11/7/2023 访问量:1280598
了解 React.js 中数组子项的唯一键
Understanding unique keys for array children in React.js
问:
我正在构建一个接受 JSON 数据源并创建可排序表的 React 组件。
每个动态数据行都有一个分配给它的唯一键,但我仍然收到以下错误:
数组中的每个子项都应该有一个唯一的“key”属性。
检查 TableComponent 的 render 方法。
我的渲染方法返回:TableComponent
<table>
<thead key="thead">
<TableHeader columns={columnNames}/>
</thead>
<tbody key="tbody">
{ rows }
</tbody>
</table>
该组件是单行,并且还为其分配了一个唯一键。TableHeader
每个 in 都是由具有唯一键的组件构建的:row
rows
<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>
)
}
});
是什么导致了唯一键属性错误?
答:
您应该为每个子项以及子项中的每个元素添加一个键。
这样 React 就可以处理最小的 DOM 更改。
在您的代码中,每个都试图在没有密钥的情况下呈现其中的一些子项。<TableRowItem key={item.id} data={item} columns={columnNames}/>
请看这个例子。
尝试从 div 内的元素中删除 (并检查控制台)。key={i}
<b></b>
在示例中,如果我们没有给元素一个键,我们只想更新 ,React 需要重新渲染整行而不是只渲染元素。<b>
object.city
代码如下:
const data = [
{ name: "Nuri", age: 28, city: "HO" },
{ name: "Talib", age: 82, city: "HN" },
{ name: "Jenny", age: 41, city: "IT" },
];
const ExampleComponent = React.createClass({
render: function () {
const infoData = this.props.info;
return (
<div>
{infoData.map((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(<ExampleComponent info={data} />, document.body);
底部@Chris发布的答案比这个答案要详细得多。
关于密钥在对账中的重要性的 React 文档:密钥
评论
迭代数组时要小心!!
一个常见的误解是,使用数组中元素的索引是抑制您可能熟悉的错误的可接受方法:
Each child in an array should have a unique "key" prop.
然而,在许多情况下,事实并非如此!这是反模式,在某些情况下可能导致不需要的行为。
了解道具key
React 使用 prop 来理解组件到 DOM 元素的关系,然后将其用于协调过程。因此,键始终保持唯一性非常重要,否则 React 很有可能会混淆元素并改变不正确的元素。同样重要的是,这些关键帧在所有重新渲染过程中保持静态,以保持最佳性能。key
话虽如此,只要知道数组是完全静态的,并不总是需要应用上述内容。但是,鼓励尽可能应用最佳实践。
一位 React 开发者在 GitHub 上说:
- 关键不是真正的性能,而是更多的身份(这反过来又会带来更好的性能)。随机分配和更改的值不是标识
- 在不知道您的数据是如何建模的情况下,我们无法[自动]提供密钥。如果您没有 id,我建议也许使用某种哈希函数
- 当我们使用数组时,我们已经有了内部键,但它们是数组中的索引。插入新元素时,这些键是错误的。
简而言之,a 应该是: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 1
key={0}
Item 2
Item 1
Item 1
key={1}
key={0}
Item 2
key={0}
因此,React 认为元素没有改变,因为 with 键总是在顶部!<input>
Item
0
那么,为什么这种方法有时很糟糕呢?
仅当以某种方式筛选、重新排列数组或添加/删除项目时,此方法才有风险。如果它始终是静态的,那么使用起来是完全安全的。例如,可以使用此方法安全地迭代导航菜单,因为您可能永远不会添加新链接或重新排列它们。["Home", "Products", "Contact us"]
简而言之,这是您可以安全地使用索引的时候:key
- 数组是静态的,永远不会改变。
- 从不筛选数组(显示数组的子集)。
- 数组永远不会重新排序。
- 该阵列用作堆栈或后进先出(后进先出)。换句话说,添加只能在数组的末尾完成(即 push),并且只能删除最后一项(即 pop)。
如果我们在上面的代码片段中将添加的项目推送到数组的末尾,则每个现有项目的顺序将始终是正确的。
很差
<tbody>
{rows.map((row) => {
return <ObjectRow key={Math.random()} />;
})}
</tbody>
虽然这种方法可能会保证键的唯一性,但它总是会强制 react 重新呈现列表中的每个项目,即使这不是必需的。这是一个非常糟糕的解决方案,因为它极大地影响了性能。更不用说,在两次产生相同数字的情况下,不能排除密钥冲突的可能性。Math.random()
不稳定的键(如由 生成的键)将导致许多组件实例和 DOM 节点被不必要地重新创建,这可能会导致性能下降和子组件状态丢失。
Math.random()
非常好
<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>
这也是一个好方法。如果您的数据集不包含任何保证唯一性的数据(例如任意数字数组),则存在键冲突的可能性。在这种情况下,最好在循环访问数据集中的每个项目之前手动为其生成唯一标识符。最好在挂载组件或接收数据集时(例如,从 props
或异步 API 调用),以便只执行此操作一次,而不是每次组件重新渲染时。已经有一些库可以为您提供这样的密钥。下面是一个示例:react-key-index。
评论
key
key
警告:数组或迭代器中的每个子项都应该有一个唯一的“key”属性。
这是一个警告,因为对于我们将要迭代的数组项,需要具有独特的相似性。
React 将迭代组件渲染处理为数组。
解决此问题的更好方法是为要迭代的数组项提供索引。例如:
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 内置的 props。
评论
只需将唯一键添加到您的组件中
data.map((marker)=>{
return(
<YourComponents
key={data.id} // <----- unique key
/>
);
})
评论
data.id
YourComponents
这是一个警告,但解决这个问题将使 Reacts 渲染速度更快,
这是因为需要唯一标识列表中的每个项目。比方说,如果该列表的某个元素在 Reacts 中的状态发生了变化,那么 React 需要弄清楚哪个元素发生了变化,以及它在 DOM 中的位置需要改变,以便浏览器 DOM 与 Reacts 虚拟 DOM 同步。React
Virtual DOM
作为解决方案,只需为每个标签引入一个属性即可。这应该是每个元素的唯一值。key
li
key
评论
key
key={i}
["Volvo", "Tesla"]
0
1
0
1
0
检查:key = undef !!
您还收到了警告消息:
Each child in a list should have a unique "key" prop.
如果你的代码是完整的,但如果你的代码是正确的,但如果打开。
<ObjectRow key={someValue} />
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 中定义唯一键的最佳解决方案: 在映射中,您初始化了名称 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>
评论
这可能对某人有帮助,也可能没有帮助,但它可能是一个快速参考。这也与上面介绍的所有答案相似。
我有很多位置使用以下结构生成列表:
return (
{myList.map(item => (
<>
<div class="some class">
{item.someProperty}
....
</div>
</>
)}
)
经过一些反复试验(以及一些挫折),在最外层的块中添加一个关键属性解决了它。另外,请注意,标签现在已替换为现在的标签。<>
<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')
);
在上面的示例中,它使用 tag 创建一个动态列表,因此由于 tag 没有唯一键,因此它显示错误。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 时的替代解决方案(react eslint 不建议这样做):
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
- 该值不能与其同级值不相同(相同)
- 不应在渲染之间更改
例如,键值可以是数据库 ID 或 UUID(通用唯一标识符)。
这里的键是手动处理的:
<tbody>
{rows.map((row) => <ObjectRow key={row.uuid} />)}
</tbody>
你也可以让 React 使用 React.Children.toArray 处理键
<tbody>
{React.Children.toArray(rows.map((row) => <ObjectRow />))}
</tbody>
我遇到了类似的问题,但不完全是。尝试了所有可能的解决方案,但无法摆脱该错误
数组中的每个子项都应该有一个唯一的“key”属性。
然后我尝试在另一个本地主机中打开它。我不知道怎么做,但它奏效了!
评论
这是一个简单的例子,我先用 react 条件再用 map,在我添加了用户 ID 以确保它是唯一的&&
key
<tbody>
{users &&
users.map((user) => {
return <tr key={user._id}>
<td>{user.username}</td>
<td><input
name="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 属性放在您的标签中,并确保每次迭代时都赋予它独特的价值
#ensure 键的值不会与其他键冲突
例
<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”属性。
通过将索引值声明给 rendering 元素内的 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
你的密钥应该是唯一的,就像一个唯一的 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 bv
Bangladesh "
},
{
"id": "2",
"name": "walton glass door",
"suplier": "walton group",
"price": "40000",
"quantity": "5",
"description":"Walton Refrigerator is the Best Refrigerator brand in
Bangladesh "
},
}
现在,我们正在映射数据并传递唯一 ID:
{
products.map(product => <product product={product} key={product.id}
</product>)
}
以下是使用 Key 属性很好地解释的 React 文档,键应该在父组件中定义,它不应该在子组件中使用。React 文档
评论
视觉解释。
- key=index(数组)的错误方式
如您所见,标签 3、标签 2 和标签 1 都重新渲染(在“元素”面板中闪烁)。
只有顶部的新元素闪烁(重新渲染)。
我认为在使用表(或类似示例)时,为了可重用性,应该将创建唯一键从父组件传递到子组件。
因为如果你正在创建一个表,这意味着你正在从父级传递数据。如果分配当前数据可能具有属性,但如果要在其他地方使用此表组件,则假定在已传递的每一行数据中,您都具有属性。key={row.name}
name
name
由于工程师将在父组件中准备数据,因此工程师应根据数据创建关键函数。
const keyFunc = (student) => {
return student.id;
};
在这种情况下,工程师知道它发送了什么数据,它知道每一行都有一个唯一的 id 属性。也许在不同的数据集中,数据集是股票价格,它没有“id”属性,而是“symbol”
const keyFunc = (stock) => {
return stock.symbol;
};
这应该作为道具传递给子组件,以保证可重用性和唯一性。keyFunc
根据 React 文档,每一行/每一项都应该有一个唯一的键。
键帮助 React 识别哪些项目已更改、添加或删除。
就我个人而言,我更喜欢使用加密接口来生成一个随机的 UUID:
(内置在 vanilla-js 中)crypto
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
<li key={crypto.randomUUID()}>item {number}
</li>
);
评论
li
当你创建一个没有特殊 key 属性的元素列表时,React 中会出现“列表中的每个子项都应该有一个唯一的 'key' prop.” 警告。必须将键分配给循环中的每个元素,以便为 React 中的元素提供稳定的身份。
我们可以将对象的 id 属性设置为唯一键。
export default function App() {
const posts = [
{ id: 1, title: "First "},
{ id: 2, title: "Second" },
{ id: 3, title: "Third" }
];
return (
<div>
<ul>
{posts.map(value =>
<li key={value.id}>{value.title}</li>
)}
</ul>
</div>
);}
//simple way
//if u using ant design remove the empty fragment...
//worng ans---> change to following crt ans
export default function App() {
const posts = [
{ id: 1, title: "First "},
{ id: 2, title: "Second" },
{ id: 3, title: "Third" }
];
{fields.map((field,index)=>{
return(
<> //empty fragment
<Row key={index}>
<Col span={6}>hello</Col>
</Row>
</>
)
})}
//correct ans
//remove the empty fragments after solve this key.prop warning problem
export default function App() {
const posts = [
{ id: 1, title: "First "},
{ id: 2, title: "Second" },
{ id: 3, title: "Third" }
];
{fields.map((field,index)=>{
return(
<> //empty fragment
<Row key={index}>
<Col span={6}>hello</Col>
</Row>
</>
)
})}
评论
key
<React.Fragment key={index}><React.Fragment />
map
我见过很多人渲染片段,它产生了这个问题。尝试将片段更改为具有唯一<></>
null
key
对于 React 官方文档
应该在里面key
<li key={person}>{person}</li>
const people = [
'Creola Katherine Johnson: mathematician',
'Mario José Molina-Pasquel Henríquez: chemist',
'Mohammad Abdus Salam: physicist',
'Percy Lavon Julian: chemist',
'Subrahmanyan Chandrasekhar: astrophysicist'
];
export default function List() {
const listItems = people.map(person =>
<li key={person}>{person}</li>
);
return <ul>{listItems}</ul>;
}
下面是示例代码 (App.jsx),只需将其用于您的文件
const ListItem = (props) => (
<li>
<input type="checkbox" />
<p> {props.title} </p>
</li>
);
const productList = [
{
id: 1,
title: 'Shirt',
checked: false
},
{
id: 2,
title: 'Pant',
checked: false
},
{
id: 3,
title: 'Cap',
checked: false
},
]
function App(props) {
return (
<>
<div>
<ul>
{productList.map((item) => (
<ListItem key={item.id} title={item.title} />
))}
</ul>
</div>
</>
)
}
export default App
我还必须处理没有唯一键的子项的删除和添加。
因此,我最终使用 UUID 作为键,并根据元素的数量将它们包装在 useMemo 中。
const uniqueKeys = useMemo(() =>
data.map(_ => uuidv4()),
[data.length]
);
// in render
{data.map((item, index) => (
<Tr key={uniqueKeys[index]}>
评论
key
rows
key
thead
tbody