Reputation: 27713
I'm getting this warning:
Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of EventsTable. See fb.me/react-warning-keys for more information.
react-runtime-dev.js?8fefd85d334323f8baa58410bac59b2a7f426ea7:21998 Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of Event. See fb.me/react-warning-keys for more information.
This is EventsTable
:
EventsTable = React.createClass({
displayName: 'EventsTable',
render() {
console.dir(this.props.list);
return (
<table className="events-table">
<thead>
<tr>
{_.keys(this.props.list[0]).map(function (key) {
if (key !== 'attributes') {
return <th>{key}</th>;
}
})}
</tr>
</thead>
<tbody>
{this.props.list.map(function (row) {
return (
<Event key={row.WhatId} data={row} />
);
})}
</tbody>
</table>
)
}
});
This is Event
:
Event = React.createClass({
displayName: 'Event',
render() {
return (
<tr>
{_.keys(this.props.data).map((x) => {
if (x !== 'attributes')
return <td>{this.props.data[x]}</td>;
})}
</tr>
)
}
});
Clearly I've got the key
prop on the <Event />
component. And I'm following the convention that you're supposed to include key
on the component, not on the HTML itself (in other words, HTML tags within the Event
component). Per the official React docs:
The key should always be supplied directly to the components in the array, not to the container HTML child of each component in the array:
I'm severely confused. Why am I getting warnings?
Upvotes: 45
Views: 76118
Reputation: 21
In this case, it seems to be thrown from return <th>{key}</th>
.
But for those who wonder from where it could be thrown in scenarios like this
return renderSelectFormItem([key], value)
wrap it with <React.Fragment>
and assign a unique key to it,
return <React.Fragment key={key}>{renderSelectFormItem([key], value)}</React.Fragment>
Upvotes: 0
Reputation: 1040
I ended up solving it when I realized because I had a <React.Fragment>
which also needs a unique key.
Upvotes: 39
Reputation: 181
The easiest fix for this is to create a separate component for the items you're mapping and add the key to that component.
Create a new component above your existing component (or link to it your call).
const TableDataComponent = ({ k }) => {
return (
<th>{k}</th>
)
}
Then in your code add that component with your key:
<tr>
{arr.map((k) => {
return <TableDataComponent key={k._id} k={k} />
})}
</tr>
Upvotes: 0
Reputation: 5333
Check if variable that you pass to key
is defined, because if it's undefined
then error will be same, but it looks like code should work.
Upvotes: 12
Reputation: 31078
Every time you render a list (use map
), add a unique key
attribute to the list elements (the topmost or "root" element returned from map
's callback):
render() {
return (
<div>
{this.props.data.map( element => {
// Place the key on to the returned "root" element.
// Also, in real life this should be a separate component...
return <div key={element.id}>
<span>Id (Name): </span>
<span>{element.id} </span>
<span>({element.name})</span>
</div>;
})}
</div>
)
}
The official Lists and Keys documentation shows how you should work with lists and the linked reconciliations doc tells the whys.
Basically when React rerenders a component it runs a diff algorithm that finds out what changed between the new and the previous version of the list. Comparison is not always trivial, but if there is a unique key in each element, it can be clearly identified what has changed. See the example in the doc:
<!-- previous -->
<ul>
<li key="2015">Duke</li>
<li key="2016">Villanova</li>
</ul>
<!-- new -->
<ul>
<li key="2014">Connecticut</li>
<li key="2015">Duke</li>
<li key="2016">Villanova</li>
</ul>
It is clear that a new element with the key 2014
was added, since we have all the other keys and those weren't changed. Without the keys this would be obscure.
From now it is easy to see:
Math.random()
.key
attribute on componentsThe convention that you should place the key
attribute to a component is more of a good practice, because when you iterate a list and want to render an element, that clearly indicates that you should organize that code to a separate component.
key
attribute in the loopThe statement you quoted from the docs:
The key should always be supplied directly to the components in the array, not to the container HTML child of each component in the array:
Means that if you render components in a loop, then you should set the key
attribute of the component in the loop, like you did it in your EventsTable
component:
{this.props.list.map(function (row) {
return (
<Event key={row.WhatId} data={row} />
);
})}
The wrong way is to pass it down to the component where it would set the key
on itself:
Event = React.createClass({
displayName: 'Event',
render() {
// Don't do this!
return (
<tr key={this.props.data.WhatId}>
{_.keys(this.props.data).map((x) => {
There is another good example for this in this article.
Upvotes: 19
Reputation: 1028
I had the problems too, and fixed it after follwing link.
like:
{_data.map(function(object, i){
return <div className={"row"} key={i}>
{[ object.name ,
<b className="fosfo" key={i}> {object.city} </b> , // remove the key
object.age
]}
</div>;
})}
Upvotes: 3
Reputation: 9597
Have you tried adding a key
to the <th>
tag?
<tr>
{_.keys(this.props.list[0]).map(function (key) {
if (key !== 'attributes') {
return <th key={key}>{key}</th>;
}
})}
</tr>
Upvotes: 32