The key prop being unique is very important for rendering list elements in React. Is it just enough if it's unique? Not really.
We talked about it briefly but lets see how we can thoroughly mess up our components if we use the key prop incorrectly. For eg, if we use index of the element in the list as key prop, we are going to run into a few issues. Let’s talk about that.
*Note: This article is a part of the Delightful React Series, as part of which, I am releasing one chapter every day for 25 days. Please support me in anyway you can! *
In this chapter, we will create a buggy Array of elements like in this sandbox and then fix it!
React Reconciling list items
Let’s talk about React’s reconciliation algorithm. Reconciliation is simply a process where React tries to reconcile with the existing DOM nodes and apply the new update to it and make as few changes as possible.
React already knows the nodes it has rendered previously and it will try to compare with the new render in memory and figures out the least set of updates to make. Let's talk about two examples in lists.
Example 1
Let us say that have an array of 3 items rendered as elements with key prop as index. Now, let us say that there was a update in the array and it now a new item was added at the end became an array of 4 items
- React tries to reconcile and tries to identify elements using the key prop.
- It notices quickly that all elements at the indices 0,1 and 2 have not changed, so it simply creates a new DOM node for the fourth item with key 3 and finishes it’s process. This part is pretty straightforward.
Example 2
- Let us start once again with an array of 3 items rendered as elements with key prop as index.
- Now, let us say that there was a update in the array but now a new item was added at the beginning of the array.
- React tries to reconcile and tries to identify elements using the key prop but now at index 0 there is an entirely new data. The text of the element is now mismatched. “Item one” vs “Item four"
- Elements at key prop 1 and 2 are also mismatched.
- This makes React very confused and the key prop is actually making things difficult and buggy here.
The bug might be hard to notice but I have a small demo that will make things super clear.
Breaking the jobs results by misusing key prop
- This is how our code looks like in
AllPosts
component.
<div className="post-list">
{filteredBlogPosts.map((blogPostObject) => {
return (
<ListItem
key={blogPostObject.title}
blogPostObject={blogPostObject}
/>
);
})}
</div>
//src/components/ListItem.js
export default function ListItem(props) {
const { blogPostObject } = props;
// const blogPostObject = props.blogPostObject
const { img, title, content = "", tags } = blogPostObject;
return (
<div className="post-list-item">
<img src={img} />
<div>
<h1>{title}</h1>
<p>{content.slice(0, 100)}</p>
<div className="tags">
{tags.map((tag) => (
<span key={tag}>{tag}</span>
))}
</div>
</div>
</div>
);
}
- Now, let’s try using the index as key prop for our filteredBlogPosts in AllPosts component.
<div className="post-list">
{filteredBlogPosts.map((blogPostObject,index) => {
return (
<ListItem
key={index}
blogPostObject={blogPostObject}
/>
);
})}
</div>
- Let’s modify the ListItem component like so and add an input into it’s mark-up.
//src/components/ListItem.js
export default function ListItem(props) {
const { blogPostObject } = props;
// const blogPostObject = props.blogPostObject
const { img, title, content = "", tags } = blogPostObject;
return (
<div className="post-list-item">
<img src={img} />
<div>
<h1>{title}</h1>
<input className="border w-64" />
<p>{content.slice(0, 100)}</p>
<div className="tags">
{tags.map((tag) => (
<span key={tag}>{tag}</span>
))}
</div>
</div>
</div>
);
}
- Once the inputs show up, enter 3 different texts into the 3 of those inputs, perhaps something like one, two and three.
Now, to finally expose the bug. Enter some text into the search field that filters out some of those list items. There! The bug is now in plain sight.
- Here is a code sandbox with the bug. Try typing some text into 3 of the ListItem inputs first and then type something into the search filter.
The items in the filteredBlogPosts array changed, but React’s algorithm completely messed up because the inputs are completely mismanaged.
The input in the first ListItem in the new render still shows one, but it should actually not be there since the text we entered in the search, removed the ListItem it was associated with.
Basically, React couldn’t figure out the right way to reuse the inputs and instead messed up completely because the index as key completely misguided React in this scenario. The key prop should uniquely identify each items in the list. And if the same key prop identifies two different items in the list across renders, all sort of errors can occur.
Hence, it is very important that as developers we make sure that we help React in such scenarios by using a key prop which is unique for all items in the list. (For eg: title of a blogpost, uuid of a user etc).
- The key prop should not only be unique for a single render, a key prop should be tied to the same list item across renders.
- If we use
index
, the key prop is '0' for whichever list item is at the first position. So while we are adding and removing elements from a list using filtering, the key '0' can be allocated to differentListItem
s at different times which will cause buggy behaviour.
<div className="post-list">
{filteredBlogPosts.map((blogPostObject) => {
return (
<ListItem
key={blogPostObject.id}
blogPostObject={blogPostObject}
/>
);
})}
</div>
- Finally, let’s fix our issue by using blogPostObject.id which we know is unique for all items in the array.
- Now, we can see that the bug is completely gone and React works perfectly fine with elements added or removed from the list.
Here is the final updated code sandbox.
Great work!
At this point, we are comfortable using the key prop with full understanding about what happens when a key prop is misused with indices. Let's talk about another very important built-in hook in the next chapter.
Thanks and Please support my work
- Writing blog posts and making videos is an effort that takes many hours in my day. I do it because I love teaching and make great content.
- However, I need your support too. Please support my work and follow my social accounts to help me continue to make great content.
- Here are my social links.
Follow me on Twiter
Subscribe to my channel on Youtube
Thank you!