我正在构建一个接受 JSON 数据源并创建可排序表的 React 组件。每个动态数据行都有一个分配给它的唯一键,但我仍然收到以下错误:
数组中的每个孩子都应该有一个唯一的“关键”道具。检查 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>
)
}
});
是什么导致了唯一键道具错误?
key
属性。它将帮助 ReactJS 找到对适当 DOM 节点的引用并仅更新标记内的内容,而不是重新渲染整个表/行。
rows
数组或更优选的 jsfiddle 吗?顺便说一下,您不需要 thead
和 tbody
上的 key
属性。
您应该为每个孩子以及孩子中的每个元素添加一个键。
这种方式 React 可以处理最小的 DOM 更改。
在您的代码中,每个 <TableRowItem key={item.id} data={item} columns={columnNames}/>
都试图在没有键的情况下在其中呈现一些子项。
检查this example。
尝试从 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 在底部发布的答案比这个答案更详细。
关于密钥在协调中的重要性的 React 文档:Keys
遍历数组时要小心!
一个常见的误解是,使用数组中元素的索引是抑制您可能熟悉的错误的可接受方式:
Each child in an array should have a unique "key" prop.
但是,在很多情况下并非如此!这是一种反模式,在某些情况下会导致不需要的行为。
了解关键道具
React 使用 key
属性来理解组件与 DOM 元素的关系,然后将其用于 reconciliation process。因此,关键 always 保持 unique 非常重要,否则 React 很有可能会混淆元素并改变不正确的元素。为了保持最佳性能,这些键在所有重新渲染中保持静态也很重要。
话虽如此,只要知道数组是完全静态的,并不总是需要应用上述内容。但是,我们鼓励尽可能采用最佳实践。
一位 React 开发人员在 this GitHub issue 中说:
关键不是真正的性能,它更多的是关于身份(这反过来会带来更好的性能)。随机分配和变化的值不是身份我们实际上无法在不知道您的数据是如何建模的情况下[自动]提供密钥。如果您没有 id,我建议您使用某种散列函数我们在使用数组时已经有内部键,但它们是数组中的索引。当您插入一个新元素时,这些键是错误的。
简而言之,key
应该是:
唯一 - 键不能与同级组件的键相同。
静态 - 一个键不应该在渲染之间改变。
使用 key 道具
根据上面的解释,仔细研究以下示例,并在可能的情况下尝试实施推荐的方法。
不好(可能)
<tbody>
{rows.map((row, i) => {
return <ObjectRow key={i} />;
})}
</tbody>
这可以说是在 React 中迭代数组时最常见的错误。这种方法在技术上并不是“错误”,如果你不知道自己在做什么,它只是......“危险”。如果您正在遍历静态数组,那么这是一种完全有效的方法(例如,导航菜单中的链接数组)。但是,如果您要添加、删除、重新排序或过滤项目,则需要小心。看看官方文档中的这个detailed explanation。
类 MyApp 扩展 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(
在这个片段中,我们使用了一个非静态数组,并且我们并不限制自己将它用作堆栈。这是一种不安全的方法(你会明白为什么)。请注意,当我们将项目添加到数组的开头(基本上是不移位)时,每个 <input>
的值保持不变。为什么?因为 key
不能唯一标识每个项目。
换句话说,一开始 Item 1
有 key={0}
。当我们添加第二项时,顶部的项变为 Item 2
,然后是 Item 1
作为第二项。但是,现在 Item 1
有 key=
而不再有 Item 1
key={0}
。相反,Item 2
现在有 key={0}
!!
因此,React 认为 <input>
元素没有改变,因为带有键 0
的 Item
始终位于顶部!
那么为什么这种方法只是有时不好呢?
这种方法只有在以某种方式过滤、重新排列或添加/删除项目时才有风险。如果它始终是静态的,那么使用它是完全安全的。例如,像 ["Home", "Products", "Contact us"]
这样的导航菜单可以安全地使用此方法进行迭代,因为您可能永远不会添加新链接或重新排列它们。
简而言之,此时您可以安全地将索引用作 key
:
数组是静态的,永远不会改变。
数组永远不会被过滤(显示数组的子集)。
数组永远不会重新排序。
该数组用作堆栈或 LIFO(后进先出)。换句话说,添加只能在数组的末尾进行(即push),并且只能删除最后一项(即pop)。
如果我们相反,在上面的代码片段中,将添加的项目推到数组的末尾,每个现有项目的顺序总是正确的。
很坏
<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>
这可以说是 best approach,因为它使用的属性对于数据集中的每个项目都是唯一的。例如,如果 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。
toString()
转换为字符串,而不是保留为数字。记住这一点很重要吗?
key
。不知道他们为什么要转换它。
key
总是最终成为一个字符串。
这可能会或不会帮助某人,但它可能是一个快速参考。这也类似于上面提供的所有答案。
我有很多使用以下结构生成列表的位置:
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
的每个子键使用唯一值,其中
该值不能与其兄弟相同(相同)
不应该在渲染之间改变
例如,键值可以是 database id 或 UUID (Universal Unique Identifier)。
这里的键是手动处理的:
<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 }:
{item.email }
我遇到了这个错误消息,因为当需要返回 null
时,数组中的某些项目会返回 <></>
。
我有一个唯一的密钥,只需将它作为这样的道具传递:
<CompName key={msg._id} message={msg} />
此页面很有帮助:
https://reactjs.org/docs/lists-and-keys.html#keys
就我而言,将 id 设置为 tag
<tbody key={i}>
问题已经解决了。
这是一个警告,但解决这个问题将使 Reacts 渲染得更快,
这是因为 React
需要唯一标识列表中的每个项目。假设如果该列表中某个元素的状态在 Reacts Virtual DOM
中发生了变化,那么 React 需要确定哪个元素发生了变化以及它需要在 DOM 中的哪个位置进行更改,以便浏览器 DOM 与 Reacts 虚拟 DOM 同步。
作为一种解决方案,只需为每个 li
标记引入一个 key
属性。此 key
应该是每个元素的唯一值。
key
属性,渲染将不会更快。如果你不提供一个,React 会自动分配一个(迭代的当前索引)。
key={i}
。所以这取决于你的数组包含的数据。例如,如果您有列表 ["Volvo", "Tesla"]
,那么很明显,Volvo 由键 0
识别,而 Tesla 具有 1
- 因为这是它们将出现在循环中的顺序.现在,如果您对数组重新排序,则交换键。对于 React,由于“对象”0
仍然在顶部,它宁愿将此更改解释为“重命名”,而不是重新排序。这里正确的键需要依次是 1
然后是 0
。您并不总是在运行时重新排序,但是当您这样做时,这是一种风险。
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`
这是一个简单的例子,我首先使用了 &&
的反应条件然后映射,在我添加了 key
用户 ID 以确保它是唯一的
<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>)
}
以下是使用 Key 属性很好解释的 React 文档,键应在父组件中定义,不应在子组件中使用。React Docs
https://i.stack.imgur.com/B0UWZ.png
https://i.stack.imgur.com/WbMzO.png
我没有详细解释,但这个答案的关键是“关键”,只需将关键属性放在您的标签中,并确保每次迭代时都赋予它独特的价值
#确保键的值不会与其他值冲突
例子
<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>
我遇到了类似的问题,但不准确。尝试了所有可能的解决方案,但无法摆脱该错误
数组中的每个孩子都应该有一个唯一的“关键”道具。
然后我尝试在不同的本地主机中打开它。我不知道如何,但它有效!
如果您正在为这个错误而苦苦挣扎,列表中的每个孩子都应该有一个唯一的“关键”道具。
通过将索引值声明给呈现元素内的键属性来解决。
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