React Keys
Overview
Whenever you render a list of elements using .map(), React strictly demands that you attach a special string or number prop called key to the outermost element returned by the loop.
Why? React's Virtual DOM uses the key prop as a unique identifier to track exactly which items in a list have changed, been added, or been removed.
Imagine a list of 1,000 tasks. If you delete the very first task, React doesn't want to destroy and recreate the remaining 999 tasks in the browser DOM (which would be incredibly slow). If every item has a unique key (like a database ID), React instantly recognizes that item task-12 was removed, and simply plucks that single DOM node out, leaving the other 999 completely untouched. Keys are fundamental to React's performance.
Syntax
function UserDirectory({ users }) {
return (
<div className="directory">
{users.map(user => (
// The key MUST be on the outermost element returned from the map!
// It must be unique among its siblings, and it must NOT change over time.
<article key={user.databaseId} className="user-card">
<h2>{user.name}</h2>
<p>{user.email}</p>
</article>
))}
</div>
);
}Common Pitfalls
- Using the Array Index as a Key: Using the map index
users.map((user, index) => <div key={index}>)is a massive anti-pattern if the list can be reordered, filtered, or deleted. If you delete the item at index 0, the item that used to be index 1 shifts to index 0. React gets confused by the changing keys and can accidentally attach the wrong state to the wrong component, causing bizarre visual bugs. - Generating Math.random() Keys: Never use
<div key={Math.random()}>. This forces the key to completely change every single time the component re-renders, causing React to destroy and rebuild the entire list from scratch every time, entirely defeating the performance purpose of the Virtual DOM.
Interview Questions
React uses keys in its Reconciliation (diffing) algorithm to uniquely identify elements across re-renders. It allows React to understand exactly which items were added, modified, or removed, allowing it to surgically update the DOM instead of wiping out and rebuilding the entire list.
Using the index is only acceptable if ALL three of these conditions are met: 1) The list is completely static and will never change or reorder. 2) The list has no unique IDs from a database. 3) The list items do not contain complex local state (like input fields).
Real-World Example
Bug Caused by Index Keys: If you type 'Needs milk' into the input next to 'Buy Groceries', and then delete the top item ('Learn React'), the 'Buy Groceries' text shifts up, but the 'Needs milk' input text stays exactly where it was in the DOM, now incorrectly sitting next to 'Go to Gym'. This is why unique, stable IDs are critical.
// BAD EXAMPLE: Using Index as Key
function TodoList() {
const [todos, setTodos] = useState(['Learn React', 'Buy Groceries', 'Go to Gym']);
const deleteFirst = () => setTodos(todos.slice(1));
return (
<ul>
{todos.map((todo, index) => (
// Because we use 'index', if we delete 'Learn React' (index 0),
// 'Buy Groceries' shifts into index 0. React thinks the TEXT changed,
// but keeps the internal state of the input box the same!
<li key={index}>
{todo} <input type="text" placeholder="Add notes..." />
</li>
))}
<button onClick={deleteFirst}>Delete Top Item</button>
</ul>
);
}Check Your Knowledge
Test your understanding of React Keys with these quick questions.