React "Warning: Encountered two children with the same key" - javascript

I have a React app that mapping cards and each card have unique id although I'm getting error in console that some are not unique:
Warning: Encountered two children with the same key, 2294264. 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.
Here is the code for building my card structure:
function CreateCards(doc) {
return (
<SimpleCard
key={doc.newsid}
theCardId={doc.newsid}
categorietitle={doc.categorietitle}
newstitle={doc.newstitle}
date={format(new Date(doc.date), "dd/MM/yyyy")}
thenews={doc.thenews}
newsurl={doc.newsurl}
/>
);
}
And here is the code for mapping the cards:
<div className="general-card1">
{this.state.noPlaceFound ? (
<h3 className="noPlaceFound">
<i className="fas fa-exclamation-circle fa-sm WarnIcon"></i>
لا يوجد نتائج
</h3>
) : (
this.state.WasMap.map((v) => CreateCards(v._source))
)}
</div>
Can you please help me?

When you render a list of components with map(), each component should have a unique key property. It is for React to distinguish them.
Rendering a todo list, for example:
{todos.map((todo) => (<li key={todo.id}>{todo.text}</li>))}
What you did wrong is that you added the key inside the component, where it should be when rendering inside map(), like so:
<div className="general-card1">
{this.state.noPlaceFound ? (
<h3 className="noPlaceFound">
<i className="fas fa-exclamation-circle fa-sm WarnIcon"></i> لا يوجد نتائج
</h3>
) : (
this.state.WasMap.map((v, index) => (<CreateCards doc={v._source} key={index} />))
)}
</div>
Notice that key = {index}. This would work, but it's better to use a custom id if you have one. You can use key = {v._source.newsid} if v._source.newsid is a unique field.
For more visit
keys section on the official documentation.

Related

"Each child in a list should have a unique "key" prop" [duplicate]

I'm building an app using the Google Books API and I appear to be passing a unique key to each child in the list, but the error won't go away. I must be doing something wrong but I'm not sure what.
const BookList = (props) => {
//map over all of the book items to create a new card for each one in the list
const books = props.books.data.items.map((book) => {
console.log(book.id);
return (
<div className="col col-lg-4 grid-wrapper">
<BookCard
key={book.id}
image={book.volumeInfo.imageLinks.thumbnail}
title={book.volumeInfo.title}
author={book.volumeInfo.authors[0]}
description={book.volumeInfo.description}
previewLink={book.volumeInfo.previewLink}
buyLink={book.saleInfo.buyLink}
/>
</div>
);
});
return <div>{books}</div>;
};
Notice that after the return in const books I have a console.log(book.id), which will display all 10 unique id keys in the console. But when I try to pass it to the child of this component using key={book.id}, I get this error.
The key needs to go on the outermost returned element. In your specific case, that means changing this:
<div className="col col-lg-4 grid-wrapper">
<BookCard
key={book.id}
to this:
<div className="col col-lg-4 grid-wrapper" key={book.id}>
<BookCard
I was using React fragments in my map() call in their simple syntax form, and was running into the same warnings with the code below:
<>
<h3>{employee.department}</h3>
<TableRow
key={employee.id}
cellValues={["Name", "Title"]} />
<TableRow
key={employee.id}
cellValues={[employee.name, employee.title]}
/>
</>
Building off the accepted answer, I realized I needed the outermost element to have the ID. I learned of an alternate syntax for React fragments that allows one to put an ID on it. The resulting code below caused the warnings to go away:
<React.Fragment key={employee.id}>
<h3>{employee.department}</h3>
<TableRow
cellValues={["Name", "Title"]} />
<TableRow
cellValues={[employee.name, employee.title]}
/>
</React.Fragment>

I have an <Add /> component that is called in two different pages, but only one of them is receiving the add count prop passed to it in React JS

Please help me understand why this logic is not working on one of the instances of my counter button. My goal is to have my counter display the count like this:
To add items to the cart, I am using this function:
const handleAddProduct = (product) => {
const productExists = cartItems.find((item) => item._id === product._id);
if (productExists) {
setCartItems(
cartItems.map((item) =>
item._id === product._id
? {
...productExists,
customer_qty: productExists.customer_qty + 1,
}
: item
)
);
} else {
setCartItems([...cartItems, { ...product, customer_qty: 1 }]);
}
};
This works well when I view the <Cart /> page because this page displays products in the "cartItems" array that have the newly added "customer_qty" key. The <Add /> component is rendering
<p> product.customer_qty </p>
The cartItems and the handleAddProduct are passed down like this:
- \<App />
- \<Cart />
- \<ShoppingCartCard /> /*Each card here is an item from "cartItems"*/
- \<Add />
However, in the normal shopping page, I cannot get the same product.customer_qty to display on the counter, since the product card instance is rendered for each item in the original array of products, so those products don't have "product.customer_qty"
For the other branch of the site, the cartItems and the handleAddProduct are passed down like this:
- \<App />
- \<Products />
- \<Specials /> /*Specials passes down product data */
- \<Carousel />
- \<ProductCard />
- \<Add />
So what I am missing is a way to have the <Add /> components in both branches, to display the same count number.
I have tried doing a setCount, but if I do it at the top <App /> level, it doesn't count individually for each product (all products get the same count). If I do it at the product card level, then then it is two different counts.
what am I missing here? The count should appear in three places: The shopping cart in the masthead, the Cart page, and at each product card.
I can share my Github repository if you'd like to see it (It's private because this is a course assignment.).
OK, so I realise that I have probably over-complicated my question. But the difficulty lies in the complex way in which data for each product is received and displayed. I have solved my problem simply by understanding the difference between what "product" means in each component (Cart and ProductCard).
in the <Cart /> component, each "product" is a synthetic duplicate of the original (created by using the spread operator "...". And is one object of a small array of products that have been added to "CartItems".
Whereas in the <ProductCard /> component, each "product" an object from a large array from the original dataset .
Originally I wanted my <Add /> component to display the value for the key "customer_qty:" which would have been counting how many items the customer wants to purchase. This displayed well in the <Cart /> component, but not in the <Products /> component.
Remember: While the <Cart /> lists items in that have already been added to the shopping cart, the <ProductCard /> will list items from the original data set.
So astute beginners or more experienced coders may see the problem: "customer_qty" does not exist if it hasn't been added to the cart.
So <Cart /> has access to this value, while <ProductCard /> will not because it is accessing the original object, which does not include "customer_qty:" in it, therefore it is null.
The solution
Create a variable with an anonymous function that checks first if the item has been added to the cart already. If it has, then quantity will be that item's "customer_qty", which exists in the cart items only. If it doesn't exist, then it is 0.
const quantity = (product) => {
let quantity;
cartItems.map((item) => {
if (item._id === product._id) {
quantity = item.customer_qty;
}
});
return quantity;
};
The complete Add component looks like this:
const Add = ({ product, handleRemoveProduct, handleAddProduct, cartItems }) => {
const productExists = cartItems.find((item) => item._id === product._id);
const quantity = (product) => {
let quantity;
cartItems.map((item) => {
if (item._id === product._id) {
quantity = item.customer_qty;
}
});
return quantity;
};
return (
<div className="counter">
{productExists ? (
<div className="show">
<div className="minus-box">
<i
className="icon-minus"
onClick={() => {
handleRemoveProduct(product);
}}
></i>
</div>
<div className="count">{productExists ? quantity(product) : 0}</div>
<div className={"add-box"}>
<i
className="icon-pink-cross"
onClick={() => {
handleAddProduct(product);
}}
></i>
</div>
</div>
) : (
<div className="first-add">
<i
className="icon-pink-cross"
onClick={() => {
handleAddProduct(product);
}}
></i>
</div>
)}
</div>
);
};
export default Add;

Why is an array that is passed correctly via props returning undefined?

I'm trying to load each string of an array of strings in a <li> html tag by passing this array via props:
<CardItem
src='https://static.news...koinex-banner.png'
text='my text'
label='Adventure'
path='/products'
description={["someText1", "someText2", "someText3", "someText4"]}
/>
function CardItem(props) {
return (
<>
<li className='cards__item'>
<Link className='cards__item__link' to={props.path}>
<figure className='cards__item__pic-wrap' data-category={props.label}>
<img
className='cards__item__img'
alt='Travel Image'
src={props.src}
/>
</figure>
<div className='cards__item__info'>
<h5 className='cards__item__text'>{props.text}</h5>
</div>
<CardDescription description={props.description} />
</Link>
</li>
</>
);
}
export default CardItem;
function CardDescription(props) {
return (
<div>
<ul>
<li>{props.description[0]} </li>
</ul>
</div>
)
}
export default CardDescription
And I'm getting
TypeError: Cannot read properties of undefined (reading '0')
I'm not sure why props.description prop is returning undefined.
Also, this TypeError seems to only be happening with the props.description prop.
Your code is misspelled CardDescrition to CardDescription
Try:
{props.description ? <CardDescription description={props.description} /> : ''}
and in description:
function CardDescription(props) {
return (
<div>
<ul>
{props.description.map(des => <li>des</li>)}
</ul>
</div>
)
}
please find the minimal repo I created:
https://github.com/snake-py/so-help-react-card
Explanation:
I try to explain from what I understand what is happening there.
When Carditems mounts it seems even though you hard code the values, that they are not passed on the initial render. Hence, the ternary check if the props include the description array.
I am guessing now why that is:
Perhaps because they are inside a wrapper component of Link. If you remove the Link component the code should work without the initial check.
well, that's probably because during the mounting of the three the description prop could be undefined, you could avoid this error by doing this props?.description[0], also if you want to render all the values in the array inside the CardDescrition component you could do this
props?.description.map((item) => (<li>{item}</li>))

Can we pass fake keys to children in React.js?

I have created a component and it's running well in local server. But I am getting below warning
Warning: Each child in a list should have a unique "key" prop.
Getting this warning means we need to fix the key index props? as given here.
below is some snippets of my component code..
render() {
return (
<div>
<Container>
<Row>
<Col className="col-12">
{this.state.client.map((val,index)=>{
if(index == this.state.colaborators.length -1)
return <a href={"/users/"+val}>{val}</a>
return <a href={"/users/"+val}>{val} ,</a>
})}
</Col>
</Row>
</Container>
</div>
</div>
</div >
);
}
}
export default App;
I checked some solution from here
As I told my code is working well. Can we use some fake key props? for example
key={fake index}
And we are using will this affect in my working code?
If this.state.client ever changes, don't just use the index (which is sadly common); see this article for why and its demo of what can go wrong. You can only do that with a list that never changes, or only grows/shrinks (and not at the same time), not with one where the order changes (you insert at the beginning, or sort, or...) More in the docs.
I'm guessing val will be unique in the list, so use that as the key:
{this.state.client.map((val, index) => {
const href = "/users/" + val;
const display = index == this.state.colaborators.length - 1 ? val : `${val} ,`;
return <a key={val} href={href} >{display}</a>;
})}
If your lists order is not going to change, simply use:
return <a key={index} href={"/users/"+val}>{val}</a>
return <a key={index} href={"/users/"+val}>{val} ,</a>
It will not affect your code and it will remove the warning.

Passing a method down two levels

I'm trying to build a simple little game for fun, but I'm hitting a stump when it comes to passing this method down to its grandchildren. I've found similar topics, but none that answered my question. For this question, I've managed to narrow the code down to what the problem is perceived to be.
The relevant component structure is App.js > Runeboard.js > Rune.js
The Goal: To have Rune.js have an onClick function on each rune that is dynamically produced by whatever is in this.state.usersRunes.
I don't believe there is anything wrong with the function itself, or passing it down as props, because console logging the runes values all succeed when in Runeboard.js, and even in Rune.
This works, but is not dynamic to what is in the this.state.usersRunes array:
return (
<div>
Runeboard
<span onClick={() => this.props.activateRune(this.props.usersRunes[0])}> {this.props.usersRunes[0]} </span>
<span onClick={() => this.props.activateRune(this.props.usersRunes[1])}> {this.props.usersRunes[1]} </span>
<span onClick={() => this.props.activateRune(this.props.usersRunes[2])}> {this.props.usersRunes[2]} </span>
<span onClick={() => this.props.activateRune(this.props.usersRunes[3])}> {this.props.usersRunes[3]} </span>
<br />
<br />
</div>
);
The problem with that, is I'd like for every item in this.state.usersRunes (an array of integers), a Rune component that has its own onClick that successfuly executes activateRune with its parameter of the value of the rune. (The value of the rune being this.state.usersRunes So this.state.usersRunes = [2,3,5,9] the values would be 2, 3, 5, and 9.
So even though the above works, this does not and I do not understand why:
App.js
The activateRune function:
activateRune(rune) {
if (this.state.inBet || this.state.mustBet) {
this.call(rune)
} else if (!this.state.inBet) {
this.setMessage("You cannot place a rune first!")
}
}
App.js render:
<Runeboard
activateRune={this.activateRune}
usersRunes={this.state.usersRunes}
/>
Runeboard.js
render:
let rune = this.props.usersRunes.map((rune, i) => {
console.log(rune) // this works and successfully prints the array's integers
return(
<div>
<Rune activateRune={this.props.activateRune}
runeValue={rune} key={i} />
</div>
)
})
return(
<div>
{rune}
</div>
)
Rune.js
render:
return (
<div onClick={() => this.props.activateRune(this.props.runeValue)}>
{this.props.runeValue} // this works and successfully displays the value
</div>
);
How can I resolve this?
I think it might be an issue with your syntax.
<div onClick={() => {this.props.activateRune(rune)}}>
If you use curly braces, {}, you need a return value:
<div onClick={() => {return this.props.activateRune(rune)}}>
You can also avoid the curly braces and have it be:
<div onClick={() => this.props.activateRune(rune)}>
Shoutout to #xiiishaneiiix and #haopeng for the fast answers! Their comments/answer helped me try out some new things, which made me test some things and narrow what the cause was.
Turns out it was within Runeboard, in a conflict with the parameter and variable name being the same.
let rune = this.props.usersRunes.map((rune, i) => {
return(
<div>
<Rune activateRune={this.props.activateRune} runeValue={rune} key={i} />
</div>
)
})
return(
<div>
{rune}
</div>
)
Having the same name, the property I was trying to pass was actually grabbing the variable with the same name, and not the parameter from map.
The solution was in renaming the variable and parameter, clearing telling the code that we want the parameter from the map and not the variable:
let runes = this.props.usersRunes.map((runeValue, i) => {
return(
<div>
<Rune activateRune={this.props.activateRune} runeValue={runeValue} key={i} />
</div>
)
})
return(
<div>
{runes}
</div>
)

Categories