React.js confuse with 'two children with the same key' - javascript

I'm using react to mounting two same components on a page,
var SelectBox = React.createClass({
getDefaultProps: function() {
return {
value: []
};
},
getInitialState: function() {
return {checked: this.props.value,count: 1};
},
handleClick: function() {
var opt = this.state.checked;
opt.push({id: this.state.count, name: this.state.count});
this.setState({checked: opt, count: this.state.count + 1});
},
render: function() {
var that = this;
var options = this.state.checked.map(item => <option key={'checked-' + that.props.name + item.id} value={item.id}>{item.name}</option>);
return (
<div>
<select multiple={true}>
{options}
</select>
<button type="button" onClick={this.handleClick}>add</button>
</div>
);
}
});
React.render(
<SelectBox name='one'/>,
document.getElementById('one')
);
React.render(
<SelectBox name='two'/>,
document.getElementById('two')
);
then click the button of the 'one', it's alright, but when i click the button of the 'two', some of 'one' crop up in 'two',why?? it make me confuse. console show:
Warning: flattenChildren(...): Encountered two children with the same key, `.$checked-two1`. Child keys must be unique; when two children share a key, only the first child will be used.
but just do some change
var a = [{id: 5, name: 5}];
React.render(
<SelectBox name='one' value={a}/>,
document.getElementById('one')
);
it work properly.
what happenned?is there something wrong or it's bug?

Oh,I find the real reason,getDefaultProps is called once and cached, the any complex objects returned by getDefaultProps() will be shared across instances, not copied, so all SelectBox components without an explicit value prop passed will share the same checked array reference in state.
in the case, i should write:
getInitialState: function() {
var tmp = this.props.value.concat();
return {checked: tmp, count: 1};
},

No, there is no bug here, you are rendering the same instance of the component twice which means that the 'components' share the same state but when you pass along different props the component now gets two states to keep track of.

This problem seems to come up for me when I set the keys in a list style react component, and the id's passed into the keys are not unique, as the error description suggests.
e.g. 2 of the list items have a key of 1.
This is usually due to an error in the logic of setting up your unique id's.
For my example I was using a redux store for the applications state, and I was setting the id of the item to be items.length + 1 which was incorrect.
This caused the two children with the same key error above for me. To fix it, I set each new item's id to be the number of itemsAdded to the list over time.
It is similar to keys in a database where the id keeps incrementing, so you can have no items in the database due to deleting them all, however your id for the next item could be 1000.

Related

How to change state of a single component in ReactJS without rendering the whole component again?

I'm new to ReactJS and have a question about how I can change the state of a specific component that was instantiated inside a Map function.
Let's say I have a simple component called panels, and inside I have N panel-item, panel-item is only a single component instantiated N times with Map function inside panels. Something like this:
class Panels extends React.Component {
constructor(props) {
super();
this.state = {
panelItems: [
{ id: '1', text: 'A' },
{ id: '2', text: 'B' },
{ id: '3', text: 'C' },
]
};
}
render() {
return (
<div>
{this.state.panelItems.map(item => (
<PanelItems key={item.id}>{item.text}</PanelItems>
))}
</div>
)
}
}
export default Panels;
Now some questions that I have:
Let's say that I wanted a button that change the state (precisely the text) of the panel item 1 to D, how can I do this?
If I did the question 1, will it re-render the whole Panels component (including the panel items 2 and 3)?
If yes, how can I only re-render the component panel item 1 without
creating a separate component to each panel item? Because they use the
same structure, only the data inside will change.
Thank you.
…a button that changes the state (precisely the text) of the panel item 1…
Create a function that splices in the change:
const updateText = (index, text) => {
// get the existing panel items from state.
// we don't want to mutate the original so we make a copy using spread syntax
const panelItems = [...this.state.panelItems];
panelItems.splice(
index, // starting at item "index"
1, // delete one item from the array
{ ...panelItems[index], text } // and insert its replacement
);
this.setState({ panelItems }); // set state with the new array
}
Then invoke that function with an onClick on the button:
<button onClick={() => updateText(0, 'the new text')}>Update Item 0</Button>
If I did the question 1, will it re-render the whole Panels component…
As a general rule, React won't re-render a pure component if props don't change. So I'd expect your example code to to work without re-rendering every PanelItem.
how can I only re-render the component panel item 1 without creating a separate component to each panel item
If you are seeing unnecessary re-renders, you might try having the loop render a component that just takes an item prop:
{this.state.panelItems.map(item => (
<Items key={item.id} item={item} />
))}
Where the Item component is what you're already doing:
const Item => ({item}) => <PanelItem>{item.text}</PanelItem>
As long as the item prop is the same object it shouldn't re-render, even when the parent state changes.

React not mapping certain objects in an array

I have to dynamically render an input form, based on the selection of a radio button. I have an array that is incremented every time the user select an radio button.
The problem is: I append an object to the array, and try to map that array on render() function. The map apparently is ignoring the object that I insert.
The select radio button code:
<MDBInput
onClick={() => {
let dependentFullName = dependent.dependentFullName;
let dependentAnswerList = this.state[dependentFullName];
let newQuestionData = {
question: thing.pergunta,
answer: true,
answerRaised: true,
info: ''
};
dependentAnswerList[thing.pergunta] = newQuestionData;
this.setState({
[dependentFullName]: dependentAnswerList
})
}}
checked={this.state[dependent.dependentFullName][thing.pergunta] ? this.state[dependent.dependentFullName][thing.pergunta]["answer"] ? true : false : false}
label='Sim'
type='radio'
id={"holder." + thing.pergunta}
/>
The map rendering code prototype:
{this.state.holder.map((question) => (<React.Fragment>
<h5>{question}</h5> <h5>{question.question}</h5></React.Fragment>))}
Try setting a state with a new reference of your mutated array:
this.setState({
[dependentFullName]: [...dependentAnswerList]
});
On state change, react makes shallow comparison with the previous one, in your case it has the same reference, therefore no render triggered.
What does setState do?.
setState() schedules an update to a component’s state object. When state changes, the component responds by re-rendering.
From setState API:
Both state and props received by the updater function are guaranteed to be up-to-date. The output of the updater is shallowly merged with the state.
A possible full-fix may look like:
const onClick = () => {
const dependentFullName = dependent.dependentFullName;
const dependentAnswerList = this.state[dependentFullName];
const newQuestionData = {
question: thing.pergunta,
answer: true,
answerRaised: true,
info: ''
};
this.setState({
[dependentFullName]: {
...dependentAnswerList,
[thing.pergunta]: newQuestionData
}
});
};
<MDBInput onClick={onClick} {...rest}/>;

Filtering Components on React Native

I'm trying to make a filtering checkbox list options to filter out the news to show on a feed. My app isn't yet getting information from database so I'm basically creating by hand to test the methods.
The variables I have are topics (which is a state prop), and feedNews, topics is an array with the topics that the user wishes to see in the feed, while feedNews are all the news components that exist to show in the feed.
e.g. of topics
this.state = {
topics: ['News-1','News-2','News-3']
}
e.g. of components for feedNews
const feedNews = [
{name:'News-1', comp: <FeedNews key={101} name="News-1" />},
{name:'News-2', comp: <FeedNews key={102} name="News-2" />},
{name:'News-3', comp: <FeedNews key={103} name="News-3" />},
{name:'News-1', comp: <FeedNews key={104} name="News-1" />},
{name:'News-3', comp: <FeedNews key={105} name="News-3" />}
]
Note: the keys on each component was just for a test
Basically what I have on my render of the class is calling a function that returns the filtered array of components:
filterFeedNews(){
let topics = this.state.topics;
return feedNews.filter((item) => {
return topics.indexOf(item.name)!==-1;
}).map(item => item.comp);
}
Now this function works everytime I open the app for the first time, but if I actually change the topics array with my filter options (list of checkboxes), sometimes there are options that disappear and they weren't the options that I had chosen. Btw, the update filter function is this:
updateFilter(newsName, value){
let newNewsTopics = this.state.topics;
if(value){
newNewsTopics.push(newsName);
}else{
newNewsTopics.splice(newNewsTopics.indexOf(newsName),1);
}
this.props.dispatch(setNewsFilter(newNewsTopics));
this.setState({
newsTopics: newNewsTopics,
});
}
this function is called by bind within each checkbox as action (because of the name of the props)
onClick(){
this.action(this.name, !this.state.value);
this.setState({
value: !this.state.value,
});
}
My question is, what am I doing wrong on those functions, or is it "normal" for this to happen in react-native
PS: if there's only one news per topic, there is no problem. This only doesn't work when there are more than one news per topic
UPDATE
I'm not sure if this is the problem, but if the problem is being ScrollView instead of ListView for filtering and rendering purposes
render(){
return (
<View style={styles.root}>
<Toolbar home={true} options={true} title='Feed' actionOptions={this.optionsAction.bind(this)}/>
<View style={styles.flexContainer}>
<ScrollView style={styles.flexContainer}>
{this.filterFeedNews()}
</ScrollView>
</View>
<View style={styles.spacing} />
{this.options()}
</View>
);
}
Solution
So basically the main wasn't even the filtering, the problem was more in terms of the rendering of the components. With the help of a colleague I had to change a bit the structure of what I posted above.
What changed:
const feedNews = [
{name:'News-1', ...otherProps},
{name:'News-2', ...otherProps},
{name:'News-3', ...otherProps},
{name:'News-1', ...otherProps},
{name:'News-3', ...otherProps}
];
added a dataSource to my state
dataSource: ds.cloneWithRows(feedNews),
my filter feed function was changed as well to adapt to the new way of thought
filterFeedNews(){
let topics = this.state.newsTopics;
let feed = feedNews.filter((item) => {
return topics.includes(item.name);
});
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.setState({dataSource: ds.cloneWithRows(feed)});
}
my update filter action had to be changed as well
updateFilter(newsName, value){
let newNewsTopics = this.state.topics;
if(value){
newNewsTopics.push(newsName);
}else{
newNewsTopics.splice(newNewsTopics.indexOf(newsName), 1);
}
this.props.dispatch(setNewsFilter(newNewsTopics));
this.setState({
newsTopics: newNewsTopics,
}, () => this.filterFeedNews());
}
and my render instead of the ScrollView it now has a ListView
<ListView
style={styles.flexContainer}
dataSource={this.state.dataSource}
removeClippedSubviews={false}
renderRow={(rowData, sectionID, rowID) => this.renderRow(rowData, sectionID, rowID)}
enableEmptySections={true}
/>
Pro: it doesn't have the problems I had with my previous approach
Con: LayoutAnimation that I used everytime the component is updated doesn't work with the ListView, so the user only has feedback of the filtration if the news in the feed have their pictures
Solution maintaining ScrollView
In case if I want to keep my initial approach with Scroll View, my solution was basically this
updateFilter(newsName, value){
let newNewsTopics = this.state.topics;
if(value){
newNewsTopics.push(newsName);
}else{
newNewsTopics.splice(newNewsTopics.indexOf(newsName), 1);
}
this.props.dispatch(setNewsFilter(newNewsTopics));
this.setState({
newsTopics: newNewsTopics,
});
}
filterFeedNews(){
let topics = this.state.topics;
let i = 0;
return feedNews.filter((item) => {
return topics.includes(item.name);
}).map((item) => {
return (<FeedNews key={++i} name={item.name}/>);
});
}
Where this.state.topics maintains the structure (an array of strings), whereas feedNews basically turns into an array of objects like in the example above for the previous solution, and then it's converted with the function filterFeedNews using filter and map o "convert" into an array of components.
In a way, the result is exactly as the same as ListView, the original animation that had isn't "working", but that's because of how the implementation is done.
Solution for the Animation
The problem I had that was causing all the problems I talked above was actually because of LayoutAnimation, everytime I "deleted" a news feed with the filtering options the Layout Animation ended up deleting more than the news feed from the specific category.
I apologize for this since I thought LayoutAnimation wasn't the problem and like that I didn't post that part of the code.
Basically for this deleting problem doesn't occur, for now the solution is to make this:
LayoutAnimation.configureNext({
duration: 300,
create: {
type: LayoutAnimation.Types.easeInEaseOut,
property: LayoutAnimation.Properties.opacity,
},
update: { type: LayoutAnimation.Types.easeInEaseOut },
});
If you are asking why I didn't put the delete, that's because in LayouAnimation delete doesn't work well on Android, only on iOS
Again, sorry for wasting your time without putting all the information
If I am not mistaken, setState does not update the state in react immediately. So in order to prevent that, I would suggest you to call the function on the callback, like the following:
this.setState({
value: !this.state.value,
}, ()=> {
this.action(this.name, this.state.value);
});
Does that makes sense?

Simple Add / Remove Component Pattern

I'm quite confused about this pattern in React. I have seen it in other places it but it does not seem correct.
Given the below code:
/** #jsx React.DOM */
var React = window.React = require('react'),
Item = require("./ui/Item");
var ItemApp = React.createClass({
getInitialState: function() {
return {
items: [ "Example Item" ] // we want one thing to render initially
}; // so prepopulate with one thing
},
getDefaultProps: function () {
return {
index: 1
}
},
onAddItem: function (e) {
var debts = this.state.debts.push( "New Item " + this.props.index);
this.setState({ items: this.state.items });
this.props.index++;
},
onRemoveItem: function (i) {
// splice here
this.state.items.splice(i, 1);
this.setState({ items: this.state.items });
this.props.index--;
},
render: function () {
var items = this.state.items;
var closeFn = this.onRemoveItem;
return (
<div className="col-lg-12">
<div className="cold-md-3"></div>
<div className="col-md-3 col-md-offset-9">
<button type="button" className="btn btn-default" onClick={this.onAddItem}><span className="glyphicon glyphicon-plus"></span> Add New Debt</button>
</div>
{items.map(function (item, i) {
return <Item name={item} closeFn={closeFn.bind(null, i)} key={i} />;
})}
</div>
);
}
});
The example above works like intended (it's a list and you can add and remove things at will) however whenever I add something renders the whole thing over again.
So after only adding one component the whole thing has rendered 3 times. After clicking twice it will have rendered 5 times. You can see why this might be a problem.
This seems really inefficient is there a better way to do this?
If you want to manually manage the dom, react probably isn't for you. You can always do it better than the abstraction if you really want to.
In react, it's usually best to aim for code that can clearly express its intent to react, and let it do its thing.
You code is close, the only problem is your key attributes don't uniquely reflect the items in the array. Index based keys are 100% fine for when you have a stack. I gave a more detailed answer to a similar question.
To actually solve this here, because any item can be removed at any time, you need to encode a unique identifier in the item itself. Instead of storing an array of strings ["a", "b", "c"] you should store an array of objects [{id: '1', text: "a"}, {id: '2', text: "b"}, {id: '3', text: "c"}]. You can then use the id as your key and it'll be more efficient.
This is unrelated to the question, but this.props.index-- is modifying props, which is bad and unreliable. If you need to keep a counter for some reason, set it directly on this in componentWillMount. If you need to communicate something up the tree, you accept a callback as a prop, and call that.
Also <Item name={item} closeFn={closeFn.bind(null, i)} key={i} /> doesn't describe any way to get data back out of the Item, other than when deletion is requested.

Understanding unique keys for array children in React.js

I'm building a React component that accepts a JSON data source and creates a sortable table.
Each of the dynamic data rows has a unique key assigned to it but I'm still getting an error of:
Each child in an array should have a unique "key" prop.
Check the render method of TableComponent.
My TableComponent render method returns:
<table>
<thead key="thead">
<TableHeader columns={columnNames}/>
</thead>
<tbody key="tbody">
{ rows }
</tbody>
</table>
The TableHeader component is a single row and also has a unique key assigned to it.
Each row in rows is built from a component with a unique key:
<TableRowItem key={item.id} data={item} columns={columnNames}/>
And the TableRowItem looks like this:
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>
)
}
});
What is causing the unique key prop error?
You should add a key to each child as well as each element inside children.
This way React can handle the minimal DOM change.
In your code, each <TableRowItem key={item.id} data={item} columns={columnNames}/> is trying to render some children inside them without a key.
Check this example.
Try removing the key={i} from the <b></b> element inside the div's (and check the console).
In the sample, if we don't give a key to the <b> element and we want to update only the object.city, React needs to re-render the whole row vs just the element.
Here is the code:
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);
The answer posted by #Chris at the bottom goes into much more detail than this answer.
React documentation on the importance of keys in reconciliation: Keys
Be careful when iterating over arrays!!
It is a common misconception that using the index of the element in the array is an acceptable way of suppressing the error you are probably familiar with:
Each child in an array should have a unique "key" prop.
However, in many cases it is not! This is anti-pattern that can in some situations lead to unwanted behavior.
Understanding the key prop
React uses the key prop to understand the component-to-DOM Element relation, which is then used for the reconciliation process. It is therefore very important that the key always remains unique, otherwise there is a good chance React will mix up the elements and mutate the incorrect one. It is also important that these keys remain static throughout all re-renders in order to maintain best performance.
That being said, one does not always need to apply the above, provided it is known that the array is completely static. However, applying best practices is encouraged whenever possible.
A React developer said in this GitHub issue:
key is not really about performance, it's more about identity (which in turn leads to better performance). randomly assigned and changing values are not identity
We can't realistically provide keys [automatically] without knowing how your data is modeled. I would suggest maybe using some sort of hashing function if you don't have ids
We already have internal keys when we use arrays, but they are the index in the array. When you insert a new element, those keys are wrong.
In short, a key should be:
Unique - A key cannot be identical to that of a sibling component.
Static - A key should not ever change between renders.
Using the key prop
As per the explanation above, carefully study the following samples and try to implement, when possible, the recommended approach.
Bad (Potentially)
<tbody>
{rows.map((row, i) => {
return <ObjectRow key={i} />;
})}
</tbody>
This is arguably the most common mistake seen when iterating over an array in React. This approach isn't technically "wrong", it's just... "dangerous" if you don't know what you are doing. If you are iterating through a static array then this is a perfectly valid approach (e.g. an array of links in your navigation menu). However, if you are adding, removing, reordering or filtering items, then you need to be careful. Take a look at this detailed explanation in the official documentation.
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>
In this snippet we are using a non-static array and we are not restricting ourselves to using it as a stack. This is an unsafe approach (you'll see why). Note how as we add items to the beginning of the array (basically unshift), the value for each <input> remains in place. Why? Because the key doesn't uniquely identify each item.
In other words, at first Item 1 has key={0}. When we add the second item, the top item becomes Item 2, followed by Item 1 as the second item. However, now Item 1 has key={1} and not key={0} anymore. Instead, Item 2 now has key={0}!!
As such, React thinks the <input> elements have not changed, because the Item with key 0 is always at the top!
So why is this approach only sometimes bad?
This approach is only risky if the array is somehow filtered, rearranged, or items are added/removed. If it is always static, then it's perfectly safe to use. For example, a navigation menu like ["Home", "Products", "Contact us"] can safely be iterated through with this method because you'll probably never add new links or rearrange them.
In short, here's when you can safely use the index as key:
The array is static and will never change.
The array is never filtered (display a subset of the array).
The array is never reordered.
The array is used as a stack or LIFO (last in, first out). In other words, adding can only be done at the end of the array (i.e push), and only the last item can ever be removed (i.e pop).
Had we instead, in the snippet above, pushed the added item to the end of the array, the order for each existing item would always be correct.
Very bad
<tbody>
{rows.map((row) => {
return <ObjectRow key={Math.random()} />;
})}
</tbody>
While this approach will probably guarantee uniqueness of the keys, it will always force react to re-render each item in the list, even when this is not required. This a very bad solution as it greatly impacts performance. Not to mention that one cannot exclude the possibility of a key collision in the event that Math.random() produces the same number twice.
Unstable keys (like those produced by Math.random()) will cause many component instances and DOM nodes to be unnecessarily recreated, which can cause performance degradation and lost state in child components.
Very good
<tbody>
{rows.map((row) => {
return <ObjectRow key={row.uniqueId} />;
})}
</tbody>
This is arguably the best approach because it uses a property that is unique for each item in the dataset. For example, if rows contains data fetched from a database, one could use the table's Primary Key (which typically is an auto-incrementing number).
The best way to pick a key is to use a string that uniquely identifies a list item among its siblings. Most often you would use IDs from your data as keys
Good
componentWillMount() {
let rows = this.props.rows.map(item => {
return {uid: SomeLibrary.generateUniqueID(), value: item};
});
}
...
<tbody>
{rows.map((row) => {
return <ObjectRow key={row.uid} />;
})}
</tbody>
This is also a good approach. If your dataset does not contain any data that guarantees uniqueness (e.g. an array of arbitrary numbers), there is a chance of a key collision. In such cases, it is best to manually generate a unique identifier for each item in the dataset before iterating over it. Preferably when mounting the component or when the dataset is received (e.g. from props or from an async API call), in order to do this only once, and not each time the component re-renders. There are already a handful of libraries out there that can provide you such keys. Here is one example: react-key-index.
This may or not help someone, but it might be a quick reference. This is also similar to all the answers presented above.
I have a lot of locations that generate list using the structure below:
return (
{myList.map(item => (
<>
<div class="some class">
{item.someProperty}
....
</div>
</>
)}
)
After a little trial and error (and some frustrations), adding a key property to the outermost block resolved it. Also, note that the <> tag is now replaced with the <div> tag now.
return (
{myList.map((item, index) => (
<div key={index}>
<div class="some class">
{item.someProperty}
....
</div>
</div>
)}
)
Of course, I've been naively using the iterating index (index) to populate the key value in the above example. Ideally, you'd use something which is unique to the list item.
Check: key = undef !!!
You got also the warn message:
Each child in a list should have a unique "key" prop.
if your code is complete right, but if on
<ObjectRow key={someValue} />
someValue is undefined!!! Please check this first. You can save hours.
Just add the unique key to the your Components
data.map((marker)=>{
return(
<YourComponents
key={data.id} // <----- unique key
/>
);
})
You should use a unique value for each children key of tbody where
the value cannot not be identical (same) to its sibling
should not change between renders
For example, the key value can be database id or UUID (Universal Unique Identifier).
Here the keys are handling manually:
<tbody>
{rows.map((row) => <ObjectRow key={row.uuid} />)}
</tbody>
You can also let React handle the keys using React.Children.toArray
<tbody>
{React.Children.toArray(rows.map((row) => <ObjectRow />))}
</tbody>
Here are the React docs that explain well using the Key property, the key should be defined at the parent component it should not be used inside the child component.React Docs
Warning: Each child in an array or iterator should have a unique "key" prop.
This is a warning as for array items which we are going to iterate over will need a unique resemblance.
React handles iterating component rendering as arrays.
Better way to resolve this is provide index on the array items you are going to iterate over.for example:
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 is React built-in props.
When you don’t have stable IDs for rendered items, you may use the item index as a key as a last resort:
const todoItems = todos.map((todo, index) =>
// Only do this if items have no stable IDs
<li key={index}>
{todo.text}
</li>
);
Please refer to List and Keys - React
In ReactJS if you are rendering an array of elements you should have a unique key for each those elements. Normally those kinda situations are creating a list.
Example:
function List() {
const numbers = [0,1,2,3];
return (
<ul>{numbers.map((n) => <li> {n} </li>)}</ul>
);
}
ReactDOM.render(
<List />,
document.getElementById('root')
);
In the above example, it creates a dynamic list using li tag, so since li tag does not have a unique key it shows an error.
After fixed:
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')
);
Alternative solution when use map when you don't have a unique key (this is not recommended by 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')
);
Live example: https://codepen.io/spmsupun/pen/wvWdGwG
If we have array object data . then we map to show the data . and pass the unique id (key = {product.id} ) because browser can select 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 mapping the data and pass the unique id:
{
products.map(product => <product product={product} key={product.id}
</product>)
}
According to React docs, each row/item should have a unique key.
Keys help React identify which items have changed, are added, or are removed.
Personally, I prefer using the crypto interface to generate a random UUID:
(crypto is built-in in vanilla-js)
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
<li key={crypto.randomUUID()}>item {number}
</li>
);
Best solution of define unique key in react:
inside the map you initialized the name post then key define by key={post.id} or in my code you see i define the name item then i define key by 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>
I was running into this error message because of <></> being returned for some items in the array when instead null needs to be returned.
I had a unique key, just had to pass it as a prop like this:
<CompName key={msg._id} message={msg} />
This page was helpful:
https://reactjs.org/docs/lists-and-keys.html#keys
In my case, set id to tag
<tbody key={i}>
The problem is solved.
A visual explanation.
The incorrect way key=index (of an array)
As you can see, label 3, label 2, and label 1 ALL got re-rendered (flashing in the Elements panel).
The correct way key=uniqueId
Only the top new element flashes (gets re-rendered).
This is a warning, But addressing this will make Reacts rendering much FASTER,
This is because React needs to uniquely identify each items in the list. Lets say if the state of an element of that list changes in Reacts Virtual DOM then React needs to figure out which element got changed and where in the DOM it needs to change so that browser DOM will be in sync with the Reacts Virtual DOM.
As a solution just introduce a key attribute to each li tag. This key should be a unique value to each element.
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>
)
}
});
This will sove the problem.
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`
This is a simple example,I have used a react condition with && first then map, in the I have added the key the user id to be sure that it's unique
<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>
your key should be unique.like an unique id.And your code should be like this
<div>
{products.map(product => (
<Product key={product.id}>
</Product>
))}
</div>
I don't go with the detail explanation but key to this answer is "key"
just put the key attribute in your tag and ensure that every-time you iterate you give unique value to it
#ensure that key's value is not clashing with others
Example
<div>
{conversation.map(item => (
<div key={item.id } id={item.id}>
</div>
))}
</div>
where conversation is an array something like below :
const conversation = [{id:"unique"+0,label:"OPEN"},{id:"unique"+1,label:"RESOLVED"},{id:"unique"+2,label:"ARCHIVED"},
]
I think when working with tables (or in similar examples), creating a unique key should be passed to child component from the parent component for the sake of REUSABILITY.
Because if you are creating a table, that means you are passing data from the parent. If you assign key={row.name} maybe currently data has name property but if you want to use this table component somewhere else you assume that in each row of data that you have passed, you have name property.
Since the engineer will be preparing the data in the parent component, the engineer should create a key function based on the data.
const keyFunc = (student) => {
return student.id;
};
In this case engineer knows what data it is sending, it knows that each row has id property which is unique. Maybe in the different data set, the data set is stock prices and it does not have "id" property but "symbol"
const keyFunc = (stock) => {
return stock.symbol;
};
this keyFunc should be passed to the child component as a prop to guarantee the reusability and uniqueness.
The "Each child in a list should have a unique "key" prop." warning happens in React when you create a list of elements without the special key attribute. Keys must be assigned to each element in a loop to give stable identity to elements in React.
We can set the id property of the object as a unique key.
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>
</>
)
})}
I faced a similar problem but not exact. Tried every possible solution and couldn't get rid of that error
Each child in an array should have a unique "key" prop.
Then I tried opening it in a different local host. I don't know how, but it worked!
If you are struggling with this error Each child in a list should have a unique "key" prop.
Solve by declaring index value to the key attribute inside the rendering element.
App.js component
import Map1 from './Map1';
const arr = [1,2,3,4,5];
const App = () => {
return (
<>
<Map1 numb={arr} />
</>
)
}
export default App
Map.js component
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

Categories

Resources