Why am I getting a warning of not having a unique key? - reactjs

This is my render
render() {
let products = this.state.products
return (
<ul>
{products.map((product, index) => Product({ key: index, product: product }))}
</ul>
);
}
I am using a unique key, and still get the warning
Warning: Each child in an array or iterator should have a unique "key"
prop.

That's not how you return a Component or pass it the key prop (or any other props...)
<ul>
{products.map((product, index) => (
<Product key={index} product={product} />
))}
</ul>
https://reactjs.org/docs/components-and-props.html#composing-components

I found this for you.
How to create unique keys for React elements?
It seems like you need to have a return for the key.
Or, as it states, npm packages already exist to declare unique keys.

You are not passing child elements to the ul
render() {
let products = this.state.products
return (
<ul>
{products.map((product, index) =>
<li key={index}>
{product}
</li>}
</ul>
);
}

I'm seeing this:
function Product(props) {
return (
<p key={props.key}>{props.product}</p>
)
}
function Main(props) {
let products = [ "foo", "bar", "baz"];
return (
<ul>
{products.map((product, index) => Product({key: index, product: product }))}
</ul>
);
}
ReactDOM.render(
<Main></Main>,
document.getElementById('example')
);
and this:
function Product(props) {
return (
<p>{props.product}</p>
)
}
function Main(props) {
let products = [ "foo", "bar", "baz"];
return (
<ul>
{products.map((product, index) => (
<li key={index}>
{Product({product: product })}
</li>
))}
</ul>
);
}
ReactDOM.render(
<Main></Main>,
document.getElementById('example')
);
Do what you're trying to accomplish.
If I had to guess (I don't have high confidence in this explanation), I would suspect that React requires a key prop on child components to so that it can quickly determine which elements need to be re-rendered when state changes. Therefore, passing a key prop won't actually achieve anything unless it's actually rendered as UI. The two examples above are rendering keys to the virtual DOM in the <p> and <li> respectively.

Related

react.js error handling inside functional component

I have a component which receives a list of items through props.
It looks like this:
const component = (props) => {
return (
<ul>
{props.list.map((item) => (
<ListItem key={item.Id} title={item.title} imgSrc={item.img.url} />
))}
</ul>
);
};
edit:
and the child looks like this:
const ListItem = (props) => {
return (
<li key={props.key}>
<h4>{props.title}</h4>
<div>
<img src={props.imgSrc} alt='thumbnail'
/>
</div>
</li>
);
};
The list comes from an API and there are cases in which the values I am assigning will be undefined or not available (imgSrc for example). This breaks the entire rendering of the app.
How can I handle errors in a way that will skip the problematic item and continue with the mapping? It usually means this is a deleted item so I wish to skip it all together.
I usually wrap the code with a try-catch or if statement but I am not allowed to do it here.
There are many options to solve that. For example, you could use the filter method before your .map call.
const component = (props) => {
return (
<ul>
{props.list.filter((item) => item.img.url !== undefined).map((item) => (
<ListItem key={item.Id} title={item.title} imgSrc={item.img.url} />
))}
</ul>
);
};
Another possible option could be Error Boundaries. I don't think that they are what you need, but it could be interesting for you anyways.
You can conditional rendering.
Array.isArray(props.list) &&
props.list.map((item) => (
<ListItem key={item.Id} title={item.title} imgSrc={item.img.url} />
));
You can only map over the array if it is an array as:
const component = (props) => {
return (
<ul>
{Array.isArray(props.list) && props.list.map((item) => (
<ListItem key={item.Id} title={item.title} imgSrc={item.img.url} />
))}
</ul>
);
};

Why doesn't `index` qualify as a unique key, when passed as part of props to a custom react component?

When I just list the items directly, using index works. As in the following.
<ol className="item-list">
{
props.items.map((item, index) => (
<li key={index}>{item}</li>
))
}
</ol>
But when I create a custom component to represent the list item, using index doesn't seem to qualify as being unique... And I end up getting a warning, as in the following.
<ol className="item-list">
{
props.items.map((item, index) => (
<ShoppingItem
index={index}
item={item}
/>
))
}
</ol>
The ShoppingItem is a simple component, like the following.
const ShoppingItem = props => (
<li key={props.index}>{props.item}</li>
);
And the warning I get in the console is the following.
Warning: Each child in a list should have a unique "key" prop.
You should read carefully the react docs for Lists and Keys: Extracting components with keys. The key goes on the component being mapped, not what it renders.
Incorrect
const ShoppingItem = props => (
<li key={props.index}>{props.item}</li>
);
<ol className="item-list">
{
props.items.map((item, index) => (
<ShoppingItem
index={index}
item={item}
/>
))
}
</ol>
Correct
<ol className="item-list">
{
props.items.map((item, index) => (
<ShoppingItem
key={index} // <-- key goes here
item={item}
/>
))
}
</ol>

Map an array inside an object inside an array React JS

I have an Array of Objects, and those objects have an object with an array in it. I want to map the "shoot: Array(6)" so I can list out the items.
How would I go about this? Im able to map the name, id, and instructions, but im having trouble getting access to and mapping the shots object then shoot array.
Current Code Information:
{Object.values(instructions).map(({id, name, Instructions}, i) => {
return (
<div key={id}>
<p><b>{name}</b></p>
<p>{Instructions}</p>
</div>
);
})}
You can map on the shoots array within each object like this:
{Object.values(instructions).map(({id, name, Instructions}, i) => {
return (
<div key={id}>
<p><b>{name}</b></p>
<p>{Instructions}</p>
{shoot.shoots.map(shoot => (<p>{shoot}</p>))}
</div>
);
})}
try this code:
{Object.values(instructions).map(({id, name, instructions, shots}, i) => {
return (
<div key={id}>
<p><b>{name}</b></p>
<p>{instructions}</p>
<p>{shots.amount}</p>
{shots.shoot.map(item => (
<div>{item}</div>
))}
</div>
);
})}
Destruct the Shots object along with {id, name, Instructions} and map the shoots array from the Shots object.
{
Object.values(instructions).map(({id, name, Instructions, Shots}, i) => {
return (
<div key={id}>
<p><b>{name}</b></p>
<p>{Instructions}</p>
{
Shots.shoots.map(shoot => (<p>{shoot}</p>))
}
</div>
);
})
}

If statement in react .map function

I'm trying to only map a function to listItems if the condition is true, otherwise skip it. I have been trying different stuff but I don't want to return before the list is complete.
I have this code:
const listItems = (
<ul>
{desclist.map((point, index) =>
if (point.length < 2) {
<li key={index}>
<p>{point}</p>
</li>
)}
}
</ul>
);
return listItems;
}
If you return something within the .map function, it won't end the loop of the function and will still continue iterating. Instead, what you return will be put within the resulting array that .map returns. Meaning that if inside the .map you return a component depending on an if statement, and otherwise none, the result will be an array with all the components that were returned.
const listItems = (
<ul>
{
desclist.map((point, index) =>
if (point.length < 2) {
return <li key={index}>
<p>{point}</p>
</li>;
}
);
}
</ul>
);
If else will work under parentheses and also don’t use index directly as key instead append some text along with index to key prop so
Change
{desclist.map((point, index) =>
if (point.length < 2) {
<li key={index}>
<p>{point}</p>
</li>
)}
}
To
{desclist.map((point, index) => {
if (point.length < 2) {
return (<li key={'Key-'+index}>
<p>{point}</p>
</li>
)}
}
)}

How to loop an array inside an array with React

Hello I have some data I am trying to display with react that looks like this
snapshot
there is an Options array inside of the Poll array.
How do I display the options array with react when I am already mapping the Poll array? This is what i have so far:
renderPost(posts){
return posts.map((post) => {
return (
<div>
<h3>{post.question}</h3>
</div>
);
});
}
Something like
renderPost(posts){
return posts.map(post =>
<div>
<h3>{post.question}</h3>
<ul>
{post.options.map(option =>
<li>{option.title}</li>
)}
</ul>
</div>
)
}
You should never forget add a key to the jsx element when generate them in loop. The key should be unique and key should be from data, if your data doesn't contain unique id per object then use index as a key like below
Unique id as key from data
return posts.map(post =>
<div key={post.id}>
<h3>{post.question}</h3>
<ul>
{post.options.map(option =>
<li key={option.id}>{option.title}</li>
)}
</ul>
</div>
)
Use index as a key if you don't have unique id in data array
return posts.map((post, index) =>
<div key={`Key${index}`}>
<h3>{post.question}</h3>
<ul>
{post.options.map((option, i) =>
<li key={`Key${i}`}>{option.title}</li>
)}
</ul>
</div>
)

Resources