If you’ve written some React code, you’ve most probably seen this warning:
Almost every React application displays an array list of some kind using the method map. And React tells us that for each element of that list that we return for rendering, we must provide a unique key prop.
But do you know why it’s necessary?
Why does React need this key prop? 🔑
The official documentation clarifies this perfectly:
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.
When the state of your component changes, the render function will return a new tree of React elements, different to the previous/current one. React needs to figure out what are the differences to efficiently update the UI with the most recent changes. This process of matching both element trees is called reconciliation.
You can read more about React’s diffing algorithm here, but the important thing for us are the lists.
So… What’s exactly the problem with lists?
Well, imagine that you render a list and you don’t provide keys for its elements:
<li>Item 1</li>
<li>Item 2</li>
Then imagine a new Item 3 is added. The new tree looks like this:
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
React now needs to compare these two trees to identify the changes made. And to do that, React iterates over both lists of children at the same time and generates a mutation whenever there’s a difference.
So it will match the first two elements, and generate a mutation for the third one. Good. So far, no problems 👍
Now, imagine that a new item is added, but at the beginning. This is the new tree now:
<li>Item 0</li> // <= New item
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
Once again, React will iterate, compare and generate mutations where needed.
When comparing the first element of the old tree (Item 1) with the first element of the new tree (Item 0), they are different, so a mutation is generated.
When comparing the second element of the old tree (Item 2) with the second element of the new tree (Item 1), they are different, so a mutation is generated.
And so on… Do you see the problem? React will mutate every child instead of realizing it can keep items 1, 2 and 3 intact. This inefficiency is a problem 😕
Key prop to the rescue!
This is why the key prop is needed, to solve this issue. When children have keys, React uses the key to match children in the old tree with children in the new tree.
<li key={0}>Item 0</li> // <= New item
<li key={1}>Item 1</li>
<li key={2}>Item 2</li>
<li key={3}>Item 3</li>
Now React knows right away that the new item is <li>Item 0</li>and the other items have just moved. 👌
Ok, got it. But will my app break if I don’t use keys? 🤔
It depends. Apart from the warning, your application won’t throw an error, but you may produce bugs if you don’t provide keys. If you change the order of the list items (either by sorting the list or removing an item, for example), weird things will happen. You’ll see it in the following example.
Let’s code to make it clearer
I’ve coded a very simple app where you are presented with a list of emojis and you can rate each of them.
If the app was just that, the absence of keys wouldn’t produce any bug. The list is static and there’s no way the items are reordered.
But to illustrate the problem, I’ve also added the possibility to remove the emojis from the list.
You can play with it here:
If you check the console, you’ll see that React is warning us about not having the key property for our list items.
To see what happens when we don’t provide a key, do the following:
- Rate only the 😘 emoji as “Very good”
- Delete the 😘 emoji
Do you see the problem now? In case you don’t want to play with the sandbox (or it doesn’t work), here is what happens:
After the deletion, the 🤪 emoji appears rated as “Very good”, which is wrong because we haven’t changed its rating at all 😦
Using index as key
You’ll see that for some simple applications, usually used to teach or explain React concepts, people will use the index as key.
Do you think this solves the problem? 🤔
Spoiler alert: it doesn’t. You can go ahead and try it, but the same bug will be produced.
As component instances are updated and reused based on their key, if the key is an index, moving an item changes it. As a result, the component will be updated in unexpected ways.
So what to do then?
You need to use some unique value as a key. In this case, I’ve included an id field to make it easier. It doesn’t have to be a number, you can use a string if you want. In this case we could even use the emoji itself as key, because each one of them appears only once in the array, but be careful about that.
However, don’t overthink it, because the data you’ll get in a real life application will most likely have a unique id field to identify each element.
If we rewrite our list item element to be like this:
<li className="emoji-item" key={emoji.id}>
Then both the warning in the console and the problem we had at runtime will be gone 🎉
So… What do you think? 😃
Was this useful? I hope it was, and please forgive me (and let me know 🙏) if there is any error in the code.
Thanks for reading ❤️
Top comments (15)
Excellent explanation, thank you so much 👏🏻
Thanks to you for your comment!
That's an excellent explanation. Bring more
Thank you very much!
Thanks. Excellent answer with clear description!
Thank you, I'm glad it helped!
Nice article, good to see the unwanted side effect in act 😉
Bell articolo, utile vedere i side effetti indesiderati senza key.
Thank you! I'm glad it was helpful!
Good explanation to an important concept
Thank you, I'm glad it helped!
In the emoji example why it deletes last one in the List.It has to be random. Isn't it?
Very helpful. You saved my day. ;)
Good explanation to a important concept !!
The best explanation so far!
Thank you very much!