[英]Understanding unique keys for array children in React.js
我正在构建一个接受 JSON 数据源并创建可排序表的 React 组件。
每个动态数据行都有一个分配给它的唯一键,但我仍然收到以下错误:
数组中的每个孩子都应该有一个独特的“关键”道具。
检查 TableComponent 的 render 方法。
我的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>
)
}
});
是什么导致了唯一的密钥道具错误?
您应该为每个 child以及 children 中的每个元素添加一个键。
这种方式 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);
React 文档中关于键在对账中的重要性: Keys
遍历数组时要小心!
一个常见的误解是,使用数组中元素的索引是抑制您可能熟悉的错误的可接受方式:
Each child in an array should have a unique "key" prop.
但是,在很多情况下并非如此! 这是反模式,在某些情况下会导致不需要的行为。
key
道具 React 使用key
prop 来理解组件与 DOM 元素的关系,然后将其用于协调过程。 因此,密钥始终保持唯一非常重要,否则 React 很有可能会混淆元素并改变不正确的元素。 为了保持最佳性能,这些键在所有重新渲染过程中保持静态也很重要。
话虽如此,只要知道数组是完全静态的,并不总是需要应用上述内容。 但是,我们鼓励尽可能采用最佳实践。
一位 React 开发人员在这个 GitHub 问题中说:
- 关键不是真正的性能,它更多的是关于身份(这反过来会带来更好的性能)。 随机分配和变化的值不是身份
- 在不知道您的数据是如何建模的情况下,我们实际上无法 [自动] 提供密钥。 如果您没有 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 1
有key={0}
。 当我们添加第二个项目时,最上面的项目变成了Item 2
,然后是Item 1
作为第二个项目。 但是,现在Item 1
有key={1}
而不是key={0}
了。 相反, Item 2
现在有key={0}
!!
因此,React 认为<input>
元素没有改变,因为键为0
的Item
总是在顶部!
那么为什么这种方法只是有时不好呢?
这种方法只有在以某种方式过滤、重新排列或添加/删除项目时才有风险。 如果它始终是静态的,那么使用它是完全安全的。 例如,像["Home", "Products", "Contact us"]
这样的导航菜单可以安全地使用此方法进行迭代,因为您可能永远不会添加新链接或重新排列它们。
简而言之,这是您可以安全地将索引用作key
的时候:
如果我们相反,在上面的代码片段中,将添加的项目推到数组的末尾,每个现有项目的顺序总是正确的。
<tbody>
{rows.map((row) => {
return <ObjectRow key={Math.random()} />;
})}
</tbody>
虽然这种方法可能会保证键的唯一性,但它总是会强制 react 重新渲染列表中的每个项目,即使这不是必需的。 这是一个非常糟糕的解决方案,因为它极大地影响了性能。 更不用说,如果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>
这也是一个很好的方法。 如果您的数据集不包含任何保证唯一性的数据(例如,任意数字的数组),则可能会发生密钥冲突。 在这种情况下,最好在迭代之前为数据集中的每个项目手动生成一个唯一标识符。 最好在安装组件或接收数据集时(例如从props
或异步 API 调用),以便仅执行一次,而不是每次重新渲染组件时。 已经有一些库可以为您提供这样的密钥。 这是一个示例: react-key-index 。
这可能会或不会帮助某人,但它可能是一个快速参考。 这也类似于上面提供的所有答案。
我有很多使用以下结构生成列表的位置:
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)来填充键值。 理想情况下,您会使用列表项独有的东西。
检查:key = undef !!!
您还收到警告消息:
Each child in a list should have a unique "key" prop.
如果您的代码完整正确,但如果打开
<ObjectRow key={someValue} />
someValue 未定义!!! 请先检查一下。 您可以节省数小时。
只需将唯一键添加到您的组件
data.map((marker)=>{
return(
<YourComponents
key={data.id} // <----- unique key
/>
);
})
您应该为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 将迭代组件渲染处理为数组。
解决此问题的更好方法是为要迭代的数组项提供索引。例如:
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 内置的道具。
当您没有渲染项目的稳定 ID 时,您可以使用项目索引作为键作为最后的手段:
const todoItems = todos.map((todo, index) =>
// Only do this if items have no stable IDs
<li key={index}>
{todo.text}
</li>
);
在 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 时的替代解决方案(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
在反应中定义唯一键的最佳解决方案:在地图中您初始化名称 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>
我遇到了这个错误消息,因为当需要返回null
时,数组中的某些项目会返回<></>
。
我有一个唯一的密钥,只需将它作为这样的道具传递:
<CompName key={msg._id} message={msg} />
此页面很有帮助:
就我而言,将 id 设置为 tag
<tbody key={i}>
问题已经解决了。
这是一个警告,但解决这个问题将使 Reacts 渲染得更快,
这是因为React
需要唯一标识列表中的每个项目。 假设如果该列表中某个元素的状态在 Reacts Virtual DOM
中发生了变化,那么 React 需要确定哪个元素发生了变化以及它需要在 DOM 中的哪个位置进行更改,以便浏览器 DOM 与 Reacts Virtual DOM 同步。
作为一种解决方案,只需为每个li
标签引入一个key
属性。 该key
应该是每个元素的唯一值。
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>
)
}
});
这将解决问题。
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`
这是一个简单的例子,我首先使用了&&
的反应条件,然后是映射,在我添加了用户 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>
if we have array object data . then we are map for showing the data . and pass the unique id (key = {product.id} ) because browser can selected the unique data
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 "
},
}
now we are maping the data and pass the unique id:
{
products.map(product => <product product={product} key={product.id}
</product>)
}
使用 uuid 库创建一个随机 UUID:
运行npm install uuid
import { v4 as uuid } from "uuid";
array.map((item) =>
<li key={uuid()}>{item}
</li>
);
我没有详细解释,但这个答案的关键是“关键”,只需将关键属性放在您的标签中,并确保每次迭代时都赋予它独特的价值
#确保键的值不会与其他值冲突
例子
<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"},
]
你的密钥应该是唯一的。就像一个唯一的 id。你的代码应该是这样的
<div>
{products.map(product => (
<Product key={product.id}>
</Product>
))}
</div>
我认为在使用表(或类似示例)时,为了可重用性,创建一个唯一键应该从父组件传递给子组件。
因为如果你正在创建一个表,那意味着你正在从父级传递数据。 如果您分配key={row.name}
可能当前数据具有name
属性,但如果您想在其他地方使用此表组件,您假设在您传递的每一行数据中,您都具有name
属性。
由于工程师将在父组件中准备数据,因此工程师应根据数据创建密钥 function。
const keyFunc = (student) => {
return student.id;
};
在这种情况下,工程师知道它正在发送什么数据,它知道每一行都有唯一的 id 属性。 也许在不同的数据集中,数据集是股票价格,它没有“id”属性,而是“symbol”
const keyFunc = (stock) => {
return stock.symbol;
};
这个keyFunc
应该作为 prop 传递给子组件
“列表中的每个孩子都应该有一个独特的‘关键’道具。” 当您创建没有特殊 key 属性的元素列表时,React 中会出现警告。 必须将键分配给循环中的每个元素,以便为 React 中的元素提供稳定的标识。
我们可以将 object 的 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>
</>
)
})}
我遇到了类似的问题,但不准确。 尝试了所有可能的解决方案,但无法摆脱该错误
数组中的每个孩子都应该有一个唯一的“关键”道具。
然后我尝试在不同的本地主机中打开它。 我不知道如何,但它有效!
如果您正在为这个错误而苦苦挣扎,列表中的每个孩子都应该有一个唯一的“关键”道具。
通过将索引值声明给呈现元素内的键属性来解决。
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
我为每个键使用 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);
}
然后将此值分配给标记:
{this.state.markers.map(marker => (
<MapView.Marker
key={this.guid()}
coordinate={marker.coordinates}
title={marker.title}
/>
))}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.