Lists and Keys
Rendering lists of data is a fundamental part of building dynamic web applications. In React, you will typically map over an array of data and return an array of JSX elements.
Rendering Multiple Components
You can build collections of elements and include them in JSX using curly braces {}.
JavaScript's map() function is the standard way to transform an array of data into an array of React elements.
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
<li>{number}</li>
);
function NumberList() {
return (
<ul>{listItems}</ul>
);
}
When you run this code, you'll see a warning in the console: "a key should be provided for list items".
The key Prop
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.
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
Why are Keys Necessary?
React uses a "diffing" algorithm to figure out how to efficiently update the UI to match the most recent tree (Reconciliation). When children of a DOM node are updated, React iterates over both lists of children at the same time and generates a mutation whenever there's a difference.
Without keys, if an item is inserted at the beginning of a list, React will mutate every child instead of realizing it can keep the existing children intact and just insert a new one at the top. This is highly inefficient and can lead to bugs, especially with input fields losing focus or keeping stale state.
Rules of Keys
- Keys must be unique among siblings. However, they don't need to be globally unique.
- Keys must not change. Don't generate keys on the fly (like
Math.random()) during render. - Use stable IDs from your data. 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:
const todoItems = todos.map((todo) =>
<li key={todo.id}>
{todo.text}
</li>
);
Indexes as Keys: An Anti-Pattern
When you don't have stable IDs for rendered items, you might be tempted to 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>
);
Using indexes for keys is discouraged if the order of items may change. This can negatively impact performance and may cause issues with component state. If the list is static, never reordered, and never filtered, using the index is acceptable, but still not ideal.
Extracting Components with Keys
A good rule of thumb is that elements inside the map() call need keys. 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.
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>
);
}