If you’ve been working through these React tutorials in order, you might have noticed this error message in your JavaScript console:
Warning: Each child in a list should have a unique "key" prop.
This tutorial talks about what that error means, and how (and why) to fix it.
React stores data in its state, and then uses that state to render React elements. These look like HTML elements (especially if you’re using JSX, like these tutorials have been), but they’re really JavaScript objects. Behind the scenes, React converts these React elements into HTML elements, which are then rendered to the DOM.
As the state changes, React’s render()
function returns a new tree of React elements, and then here’s the important part: React only updates parts of the DOM that have changed.
Here’s an example:
See the Pen by Happy Coding (@KevinWorkman) on CodePen.
Use your browser’s developer tools to inspect the elements in this example, and then try clicking the button. (Click here to open the example in a new tab.)
The render()
function of the App
component returns a few React elements, which are then converted to HTML elements and added to the DOM. But notice that only the <p>
element in the middle changes, and the rest of the DOM stays the same.
This is part of the magic of React: as a component’s state changes, React only modifies parts of the DOM that have changed, and leaves the rest of the DOM alone.
Now you’ve seen that React only updates parts of the DOM that have changed.
Here’s another example:
See the Pen by Happy Coding (@KevinWorkman) on CodePen.
Use your browser’s developer tools to inspect the elements in the list, and then try adding and removing items. (Click here to open the example in a new tab.)
Notice where the DOM changes as the state changes. Specifically, notice that when you add or remove an item from the beginning of the list, every subsequent list element also changes, even though their content isn’t actually changing.
That’s because React isn’t smart enough to understand when content moves but doesn’t change. If you have a list like this:
And then you add an item to the beginning of the list:
…React will see that Apples
changed to Oranges
, Bananas
changed to Apples
, Strawberries
changed to Bananas
, and that a new Strawberries
item was added to the end. And since React thinks that the content of ever list item has changed, it updates every <li>
element in the DOM.
That might be okay for a small page like this. But as your page becomes more complicated, updating the DOM becomes more expensive. To help avoid unnecessary updates, React uses keys to track elements that might move and change.
To use keys, add a key
attribute to elements that are generated from a loop or list. The value can be anything you want, but each sibling element should have a unique key value. Key values can be the underlying ID in the data, an ID that you increment over time, or a UUID.
If the state contains an array of items, and each item already has an ID, here’s how you might use those IDs as keys:
<ul>
{
this.state.items.map((item) =>
<li key={item.id}>
{item.label}
</li>
)
}
</ul>
Here’s the same example from before, this time with keys added:
See the Pen by Happy Coding (@KevinWorkman) on CodePen.
Use your browser’s developer tools to inspect the elements in the list, and then try adding and removing items.(Click here to open the example in a new tab.)
Notice that only the added or removed element changes, and the rest of the DOM stays the same. That’s because React is now using the keys to determine which parts of the DOM have actually changed.
When choosing a value for your keys, you might be tempted to use a loop index as the key value. After all, you’re already looping over an array, so you might as well use the index, right?
And yes, you could do something like this:
<ul>
{
this.state.items.map((item, index) =>
<li key={index}>
{item.label}
</li>
)
}
</ul>
However, this approach won’t actually prevent React from re-rendering every item! Here’s why: Let’s say you started with these items and keys:
Index | Label |
---|---|
0 | Apples |
1 | Bananas |
2 | Strawberries |
And then your data changed to include a new item:
Index | Label |
---|---|
0 | Oranges |
1 | Apples |
2 | Bananas |
3 | Strawberries |
If you’re using the indexes as keys, React’s rendering logic will go something like this:
0
had a content of Apples
, but now it has a content of Oranges
. That means I need to re-render it!1
had a content of Bananas
, but now it has a content of Apples
. That means I need to re-render it!In other words, you’re back to the original problem of React not being smart enough to tell the difference between an element that has moved and an element that has changed.
So when in doubt, don’t use indexes as keys!
See the Pen by Happy Coding (@KevinWorkman) on CodePen.
Use your browser’s developer tools to inspect the elements in the list, and then try marking a task as completed.(Click here to open the example in a new tab.) Notice how the DOM only updates the elements that have changed!
How (and Why) to use Keys in React.
Happy Coding is a community of folks just like you learning about coding.
Do you have a comment or question? Post it here!
Comments are powered by the Happy Coding forum. This page has a corresponding forum post, and replies to that post show up as comments here. Click the button above to go to the forum to post a comment!