React Lists & Keys
We can use the JavaScript map() method to create lists.
React Example
Using the map() method to iterate over an array and generate a list of numbers from 1 to 5:
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
<li>{number}</li>
);
ReactDOM.render(
<ul>{listItems}</ul>,
document.getElementById('example')
);
We can refactor the above example into a component that accepts an array as a prop, assigning a key to each list item, otherwise, a warning will appear: a key should be provided for list items
, meaning a key is required:
React Example
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('example')
);
Keys
Keys help React identify which items have changed, are added, or are removed. Therefore, you should give a definite identifier to each item in an array.
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
A key for an element should ideally be a unique string in the list. Typically, we use IDs from the data as keys:
const todoItems = todos.map((todo) =>
<li key={todo.id}>
{todo.text}
</li>
);
When an element does not have a definite ID, you can use its index as a key:
const todoItems = todos.map((todo, index) =>
// Only use index when there is no definite ID
<li key={index}>
{todo.text}
</li>
);
If the list can be reordered, we do not recommend using indexes for keys as it can lead to slow rendering.
Extracting Components with Keys
A key's significance is only within the context of its siblings.
For instance, if you extract a ListItem component, you should place the key on the <ListItem />
element in the array, not on the <li>
element within the ListItem component.
Incorrect Example
function ListItem(props) {
const value = props.value;
return (
// Wrong! You don't need to specify the key here:
<li key={value.toString()}>
{value}
</li>
);
}
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
// Wrong! The key should be specified here:
<ListItem value={number} />
);
return (
<ul>
{listItems}
</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('example')
);
Correct Usage of Keys
React Example
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 here:
<ListItem key={number.toString()} value={number} />
);
return (
<ul>
{listItems}
</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('example')
);
Sure! Here is the English translation of the given Chinese text:
// Correct! No need to specify a key here:
return <li>{props.value}</li>;
}
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
// Correct again! The key should be specified in the context of 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('example')
);
When you call elements inside the map() method, it's best to always remember to add a unique key to each element.
Keys Should Be Unique Among Siblings
Keys used within arrays should be unique among their siblings. However, they do not need to be globally unique. When we generate two different arrays, we can use the same keys.
React Example
function Blog(props) {
const sidebar = (
<ul>
{props.posts.map((post) =>
<li key={post.id}>
{post.title}
</li>
)}
</ul>
);
const content = props.posts.map((post) =>
<div key={post.id}>
<h3>{post.title}</h3>
<p>{post.content}</p>
</div>
);
return (
<div>
{sidebar}
<hr />
{content}
</div>
);
}
const posts = [
{id: 1, title: 'Hello World', content: 'Welcome to learning React!'},
{id: 2, title: 'Installation', content: 'You can install React from npm.'}
];
ReactDOM.render(
<Blog posts={posts} />,
document.getElementById('example')
);
Keys serve as a hint to React but are not passed to your components. If your component needs the same value as the key, pass it explicitly as a prop:
const content = posts.map((post) =>
<Post
key={post.id}
id={post.id}
title={post.title} />
);
In the above example, the Post component can read props.id, but not props.key.
Embedding map() in JSX
In the previous example, we declared a separate listItems variable and included it in JSX:
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<ListItem key={number.toString()}
value={number} />
);
return (
<ul>
{listItems}
</ul>
);
}
JSX allows embedding any expressions within curly braces, so we can use map() inline:
React Example
function NumberList(props) {
const numbers = props.numbers;
return (
<ul>
{numbers.map((number) =>
<ListItem key={number.toString()}
value={number} />
)}
</ul>
);
}
This approach can sometimes make your code clearer, but this style can also be misused. Just like in JavaScript, whether you need to extract a variable for readability is entirely up to you. But remember, if a map() is nested too deeply, you can extract the component.