Two children with the same key in React [duplicate] - reactjs

This question already has answers here:
React Warning: flattenChildren(...): Encountered two children with the same key
(4 answers)
Closed 4 years ago.
Application works, my classes really adds a new element but I see below warning in console!
Warning: Encountered two children with the same key, [object
Object]. Keys should be unique so that components maintain their
identity across updates. Non-unique keys may cause children to be
duplicated and/or omitted — the behavior is unsupported and could
change in a future version.
in div (created by ContentBody)
in ContentBody
Here is my render part :
return (
<div ref={this.myRef} style={this.state.myHomeStyle} >
{this.state.elements.map((i: any) => {
console.log(">>i>>>>", i);
return <span style={i.myStyle} key={i} >{i}</span>;
})}
</div>
);
// Where i init
public componentDidMount() {
console.log('componentDidMount');
this.myDOM = this.myRef.current;
this.myDOM.addEventListener(myEventsList.adaptCss, this.adaptCss);
this.add(12,this.INLINE_TEST_ELE, null);
this.add(13,this.INLINE_TEST_ELE, null);
}
// Function add
private add = (id: number, content: any, event: any ) => {
let localArr: any[] = [];
let mEvent: any = null;
if (event !== undefined) {
mEvent = event;
}
localArr = this.state.elements;
localArr.push(React.createElement("div", { key: id , onClick : mEvent }, content));
this.setState(
{
elements: localArr,
visibility : true,
},
);
}
Any suggestions?
Update:
Here is the link for my starter project:
https://github.com/zlatnaspirala/react-vs-typescript-starter

You can pass another parameter within your map function like so:
this.state.elements.map((element, index) => {
return <span style={element.myStyle} key={index} >{element}</span>;
});
The second parameter of the Array.prototype.map function actually contains the current index of the particular element in that array.
This way, you'll be sure that your key is not duplicated.

You are passing element not index. And if your element is same then the error is being thrown. To pass the index use second param:
.map((element, index)=>
Now, using index will give you different key.

See this for more understanding in "key" related warnings and best practices
function ListItem(props) {
// Correct! There is no need to specify the key here:
return <li>{props.value}</li>;
}
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
// Correct! Key should be specified inside the array.
<ListItem key={number.toString()}
value={number} />
);
return (
<ul>
{listItems}
</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
Visit this link https://reactjs.org/docs/lists-and-keys.html#extracting-components-with-keys for more information

Related

How to fix "Each child in a list should have a unique "key" prop" in a function in React (TypeScript)

I got the following warning:
Warning: Each child in a list should have a unique "key" prop.
Check the render method of TabContext. See
https://reactjs.org/link/warning-keys for more information.
in TabForGroupInList (at Product.tsx:148)
in TabContext (at Product.tsx:144)
Here it is the render method of TabContext:
export default function TabContext(props: any) {
const selectedTabId = useSelector((state:any) => state.selectedTabId[props.id]);
...
return (
<>
{props.tabContent(selectedTabId, onChangeHandler)}
</>
);
And here TabContext is called:
export default function Product() {
const iter = (node: any, name: string) => {
let c = child(node, name);
let result: any = [];
c.items.forEach(
(e: any) => result.push(nodes[e])
);
return result;
}
return (
<>
<TabContext id={id(rootNode, 'Multi_Page1')} tabContent ={(selectedTabId:any,
changeHandler:any)=>(
<Tabs value={selectedTabId} changeHandler={changeHandler}>
{iter(rootNode, 'Multi_Page1').map((Multi__Page1: any, index: number) => { return (
<TabForGroupInList value={Multi__Page1.id} id={Multi__Page1.id} tabId={rootNode.id} />
)})}
</Tabs>
</>
)
}
I need to find a way to iterate thru props.tabContent() (TabContext), in order to get the different tabId props (TabForGroupInList) as a key in the render method of TabContext, but props.tabContent() is not an array, it is a function and I cannot use map(). Or is there any other way to get unique keys from props.tabContent()?
add key={index} or any unique identifier to your TabForGroupInList component.
Keys help React identify which items have changed, are added, or are removed. Keys should be given to the elements inside the array to give the elements a stable identity. It's quite often to see the key prop when we are iterating an array with methods like map (source)
The key prop is available in every React component you create out of the box

how to render array in object in array? (react)

const checked = [{
food:['apple', 'banana']
drink:['wine', 'beer']
}];
render (
<>
{checked.map((value) => {
value.food.forEach((each) => (
<div>{each}</div>
)
)}
</>
)
I tried this way and noting is shown in browser..
what would be the best way to approach?
Need to Return Your data like below!!
import React from "react";
export default function App() {
let checked = [{
food:['apple', 'banana'],
drink:['wine', 'beer']
}];
return (
<div className="App">
{
checked.map((item) => {
return item.food.map((fruit)=>{
return <h1>{fruit}</h1>
})
})
}
</div>
);
}
Your code has multiple errors.
It should be render instead of rander
While defining object, multiple properties should be separated using a comma. So put comma after the food array.
forEach doesn't return any thing. It just iterates over an array. So, if you want to return something (in this case a div element), use map.
Also, you should use key for each div element otherwise react would give you a warning in the console. This is done so that while re-rendering, based on the keys, react would understand which component to re-render and which to skip. Otherwise all the div would be re-rendered which is a costly operation.
const checked = [
{
food: ["apple", "banana"],
drink: ["wine", "beer"]
}
]
return (
<>
{checked.map((value) => {
return value.food.map((each, index) => {
return <div key={index}>{each}</div>;
});
})}
</>
);
There is a couple of improvements that require to be implemented to make the list displayed.
First, the map method does not return anything.
Two solutions:
Remove the curly brackets checked.map((value) => value...
Add a return keyword: checked.map((value) => { return value...}
The other issue is that the second loop is iterated using the forEach method.
The difference between the two (forEach and map) from MDN:
The forEach() method executes a provided function once for each array
element.
MDN
The map() method creates a new array populated with the results of
calling a provided function on every element in the calling array.
MDN
Basically, it means that forEach does not return anything and that why you need to use map
checked.map((value) => {
return value.food.map((each) => (<div>{each}</div>))
})}
or
checked.map((value) =>
value.food.map((each) => (<div>{each}</div>))
)}
You are iterating over the checked array items using forEach which won't induce any results since the forEach method
executes a provided function once for each array element.
which won't result in a transformed array.
What you are looking for is the map method which
creates a new array populated with the results of calling a provided function on every element in the calling array.
hence returning your transformed items so that they can be rendered (transformed at compilation time to ReactElement using the JSX syntax).
Note that you need to use an HTML tag instead of a React.Fragment the empty tag <> syntax:
const checked = [{
food:['apple', 'banana'], // there is a missing comma here
drink:['wine', 'beer']
}];
render ( // render and not rander
<div> // div instead of empty tag
{checked.map((item) => item.food.map((each) => <div>{each}</div>))}
</div>
)
Can check this approach. if you want to print just food values, below code should work. If you want to print all the values (both food and drink), then uncomment the commented code below.
export default function App() {
const checked = [
{
food: ["apple", "banana"],
drink: ["wine", "beer"]
},
{
food: ["grapes", "oranges"],
drink: ["coke", "sprite"]
}
];
// to display values of all arrays.
// return (
// <>
// {checked.map((value) => {
// const keys = Object.keys(value);
// return keys.map((eachKey) => {
// return value[eachKey].map((individualValue) =>
// (
// <div>{individualValue}</div>
// )
// )
// });
// })
// }
// </>
// );
// To display just food values.
return (
<>
{checked.map((value) => {
return value.food.map((each) => <div>{each}</div>);
})}
</>
);
}

React: how to forward refs to multiple children?

I'm trying to forward multiple refs to multiple children DOM nodes:
I need references to the 7 buttons, so I can manage focus between them.
I tried by using an array of React.createRef() and then attach each element of this array to a child using index, but all refs refers to the last button.
Why and is there another solution?
class RestaurantsList extends Component {
references = Array(7).fill(React.createRef());
render() {
return (
<ul
id="restaurants-list"
role="menu"
>
{
this.props.restaurants.map((restaurant, index) => {
return (
<Restaurant
ref={this.references[index]}
name={restaurant.name}
/>
);
})
}
</ul>
);
}
}
const Restaurant = React.forwardRef((props, ref) => {
return (
<li>
<button
ref={ref}
>
{name}
</button>
</li>
);
})
As it was discussed in comments the best way is to keep list of references as an array or object property inside parent component.
As for Array.prototype.fill() its arguments is calculated just once. In other words fill(React.createRef()) will generate list where each entry will refer to the same object - and you will get equal ref to last element. So you need to use .map() for getting unique reference objects.
references = Array(7).fill(0).map(() => React.createRef());
Anyway in real world project this will rather happen in constructor() or componentDidUpdate().
But I believe it's better to have hashmap:
references = {};
getOrCreateRef(id) {
if (!this.references.hasOwnProperty(id)) {
this.references[id] = React.createRef();
}
return this.references[id];
}
render() {
return (
....
{
this.props.restaurants.map((restaurant, index) => {
return (
<Restaurant
ref={this.getOrCreateRef(restaurant.id)}
key={restaurant.id}
name={restaurant.name}
/>
);
})
}
Also you will need some helper methods to avoid exposing this.references to outer world:
focusById(id) {
this.references[id].current && this.references[id].current.focus();
}
And take special attention to cleaning up references to unmounted elements. Otherwise you may got memory leak if list of restaurants is changed dynamically(if ref stays in this.references it keeps reference to HTML element even if it has been detached). Actual need depends on how is your component used. Also this memory leakage will be fixed once container(that has reference = {}) is unmounted itself due to navigating away.

Remove warning on captcha using reactjs

I'm working on a captcha using reactjs and ant design. Its the code is working, its just there's a warning that I cant solve.
Looks like it will be a problem in the future.
Thanks,
hope you understand me.
Warning: Encountered two children with the same key, u. Keys should
be unique so that components maintain their identity across updates.
Non-unique keys may cause children to be duplicated and/or omitted —
the behavior is unsupported and could change in a future version.
SAMPLE CODE
let numbers = [];
function NumberList() {
var rand = Math.random()
.toString(36)
.substr(2, 4);
var a = rand.split("").join(",");
var array = a.split(",");
numbers = array;
const listItems = numbers.map(number => (
<div key={number.toString()}>{number}</div>
));
return <div className="randomChars">{listItems}</div>;
}
// Captcha.
class Captcha extends Component {
componentDidMount() {
this.props.onRef(this);
}
componentWillUnmount() {
this.props.onRef(undefined);
}
fetchCaptcha() {
let concatNumbers = numbers.join("");
return concatNumbers;
}
render() {
return (
<div className="Captcha" ref="childref">
<NumberList />
</div>
);
}
If any characters are the same the number.toString() will be the same, and if keys aren't unique that warning is shown,
An alternative way to make sure that keys are unique is to pass index in map function, so change:
const listItems = numbers.map(number => (
<div key={number.toString()}>{number}</div>
));
To:
const listItems = numbers.map((number, index) => (
<div key={index}>{number}</div>
));

React key in li

I have a warning: Each child in an array or iterator should have a unique "key" prop.
But I used a key.
This is my code:
return (
<li onClick={this.handleOnMarkAsCompleted} key={Date.now()}>
{ completed ? <b>{value}</b> : value }
</li>
)
Any Ideas? Why doe it happen?
consider these two examples:
const Item = ({ i }) => <li key={i}>li</li>;
const List = ({ data }) => <ul>{data.map((_, i) => <Item i={i} />)}</ul>;
in this case, you'd get:
Warning: Each child in an array or iterator should have a unique "key" prop.
because li is not an array item. it's inside of Item which is an array item.
So key on Item would eliminate the problem:
const Item = ({ i }) => <li key={i}>li</li>;
const List = ({ data }) => <ul>{data.map((_, i) => <Item key={i} />)}</ul>;
code sandbox: https://codesandbox.io/s/oojwjq0lj6
from docs:
Keys only make sense in the context of the surrounding array.
For example, if you extract a ListItem component, you should keep the
key on the <ListItem /> elements in the array rather than on the <li>
element in the ListItem itself.
a note regarding use of Date.now():
Keys should be stable, predictable, and unique. 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.
Date.now() generates current time UNIX timestamp which is the same every time (in the second which all items are rendered). Key neeeds to be unique, as described in the error. Add some kind of id or (if no alternative is possible) an iterator.
As per ReactJS docs, Keys only make sense in the context of the surrounding array.
Example of Correct key usage.
function ListItem(props) {
// Correct! There is no need to specify the key here:
return <li>{props.value}</li>;
}
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
// Correct! Key should be specified inside the array.
<ListItem key={number.toString()}
value={number} />
);
return (
<ul>
{listItems}
</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
Try it on codepen
You need to use the key prop at the parent of this component. Lets suppose your component is named TaskItem, and the component in which you are using it is called TaskItems:
const TaskItems = (props) => { return ( <ul> {props.items.map((itemElement) => ( <TaskItem key={itemElement.id} ></TaskItem> ))} </ul> ); };
You can use index of array or keys of object row
Date.now is duplicate

Resources