Disclaimer: This is a gross oversimplification and should be used as a basic guide on how reconciliation works, not a perfect example!
When handling arrays in React, utilisation of the 'key' attribute on each element can be crucial for avoiding needless rerender performance hits. This article will hopefully explain why you should always clearly define your keys, and what you are missing out on if you are not doing so.
Let us start with an array
const joshs = [{ Name: "Josh", }, { Name: "Joshina", }, { Name: "Notjosh", }]
the business end of our React component which renders said array
<div>
{ joshs.map((person, index) => ( <span key={index}>{person.name}</span>)) }
</div>
and the HTML it outputs
<div>
<span key=’0’>Josh</span>
<span key=’1’>Joshina</span>
<span key=’3’>Notjosh</span>
</div>
Works great!
...but!
A new Josh has arrived, and he's told the others to move out of the way.
[{ Name: "Mega Josh"}, { Name: "Josh", }, { Name: "Joshina", }, { Name: "Notjosh", }]
Our component receives the new list, does its thing...
<div>
{ joshs.map((person, index) => ( <span key={index}>{person.name}</span>)) }
</div>
and prints it out like it did before.
<div>
<span key=’0’>Mega Josh</span>
<span key=’1’>Josh</span>
<span key=’2’>Joshina</span>
<span key=’3’>Notjosh</span>
</div>
Works great!
...but!
Let us look under the hood on what's actually happening (in a very simplified fashion) with the process that React is going through when it renders its new list.
A React component, when you boil it down to its rawest form (i.e. convert it from JSX), is just an object with a set of properties. These properties define its type, name, state, what props it has received, whether it has children, etc.
Each time a change occurs in our array, a new list of Josh <span>
component objects are created. The React reconciler will compare the newly created objects with the current versions it has in the DOM. If any differences are detected between certain properties, it will redraw the components believing that it's the same object, but properties have changed.
So with our example, we have our original array (of components), which can loosely be translated to something like this...
[{
Type: "span",
Key: "0",
Children: "Josh"
}, {
Type: "span",
Key: "1",
Children: "Joshina"
}, {
Type: "span",
Key: "2",
Children: "Notjosh"
}]
The reconciler will look at the key, and the component properties (in our simplified case, the content or children), and then look through its previous list of components to see if it matches any previous combinations.
As our list uses the array index position as its key, when 'Mega Josh' arrives and shifts all the components down one position, every comparison now fails due to React noticing that the keys do not match their previous properties!
[{
Type: "span",
Key: "0", // Expected 0 to match 'Josh'
Children: "Mega Josh" // IM DIFFERENT, REDRAW ME
}, {
Type: "span",
Key: "1", // Expected 1 to match 'Joshina'
Children: "Josh" // IM DIFFERENT, REDRAW ME
}, {
Type: "span",
Key: "2", // Expected 2 to match 'Notjosh'
Children: "Joshina" // IM DIFFERENT, REDRAW ME
}, {
Type: "span",
Key: "3", // IM NEW
Children: "Notjosh" // DRAW ME
}]
But! We can prevent this. If we clearly define a key which is static, unique, and uniquely associated with the properties it is related to, React can acknowledge that it's the same component, even when it has changed its position.
Let us rebuild our components with unique keys
n.b. In this example I use the name
attribute to keep our josh objects simple, but this is not best practice as the likelihood of two components having the same key is quite high. Where possible you should always use some sort of primary key from the data object.
<div>
{ people.map((person, index) => ( <span key={`key-${person.name}`}>{person.name}</span>)) }
</div>
the exported HTML will now look like
<div>
<span key=’key-Josh’>Josh</span>
<span key=’key-Joshina’>Joshina</span>
<span key=’key-Notjosh’>Notjosh</span>
</div>
and the updated array HTML
<div>
<span key='key-Mega Josh'>Josh</span>
<span key=’key-Josh’>Josh</span>
<span key=’key-Joshina’>Joshina</span>
<span key=’key-Notjosh’>Notjosh</span>
</div>
The keys are now unique to their data object (rather than their array position), so when we do our object comparison
[{
Type: "span",
Key: "key-Josh",
Children: "Josh"
}, {
Type: "span",
Key: "key-Joshina",
Children: "Joshina"
}, {
Type: "span",
Key: "key-Notjosh",
Children: "Notjosh"
}]
the reconciler will see that some components haven't changed, they have simply moved, only the new component will be created and inserted at the front of the list, not affecting the components after it. Brilliant!
[{
Type: "span",
Key: "key-Mega Josh", // IM NEW
Children: "Mega Josh" // DRAW ME
}, {
Type: "span",
Key: "key-Josh", // Expected 'key-Josh' to match 'Josh'
Children: "Josh" // IM THE SAME, DONT REDRAW ME
}, {
Type: "span",
Key: "key-Joshina", // Expected 'key-Joshina' to match 'Joshina'
Children: "Joshina" // IM THE SAME, DONT REDRAW ME
}, {
Type: "span",
Key: "key-Notjosh", // Expected 'key-Notjosh' to match 'Notjosh'
Children: "Notjosh" // IM THE SAME, DONT REDRAW ME
}]
For some uses, the performance impact may be minimal (or even non-existant if the array never changes order). The benefits with adding keys will be noticeable with very large arrays that get sorted/re-ordered as it will eliminate the need for a majority of your list to be rendered. Magic!
Top comments (9)
Regarding the opening statement - '...utilisation of the 'key' attribute on each element can be crucial for avoiding needless rerender performance hits.', am I right to say that the 'key' attribute doesn't prevent or reduce the number of times a component is re-rendered? Instead, it's more of making React's life easier when it comes to deciding what change is needed to update the browser DOM?
Love the explanation btw!
Very nice explanation, finally understood :)
Typo in 3rd image / Code snippet
last span should have key = '2'.
Great explanation! I now finally understand why keys are needed when rendering component arrays!
I felt this article like some "explain to me like I'm 5" tutorial so it was very clear for me, thank you for this post.
Finally understood!!! Thanks so much!
Finally. Went through a lot of jibberish on React Keys. This article explains it in the most simple and clear way
So using items ID is supposed to be better as it is unique in the DB.
Great explanation!