Working with List and Key Prop in React
Overview
When developing a react app, you are bound to come across list components in one form or another. There are plenty of use cases for the lists component. Even a simple Todo list app, there is a list of tasks to complete, a shopping cart showing the list of items or a home page of a social media app that keeps showing you posts on scrolling. Thus the lists component should be developed for optimal performance, and for that, we have the keys props.
Introduction
Lists are very standard when it comes to UI development in react. Every application has some repeating content structure, and hence every beginner comes across lists component. If you have just started making apps using react framework, working with lists can be tricky, but later it will be effortless. It just needs a proper explanation.
Lists are the most common component in react and can take a toll on the app’s performance if not used correctly. Because performance is one of the most important parameters, when you are using lists, you need to develop components correctly. React provides us with key props in list, to optimize the performance.
A key is a unique attribute that needs to be included in lists. It is important in the aspect of re-rendering collections. In simple words, React uses keys to pinpoint which of the element in the list collection is changed and needs to be re-rendered instead of re-rendering the whole collection. Therefore boosting the performance of the react application. That's why we will be learning how to correctly design list components in JSX and the best practices that should be followed.
Rendering a Simple List Component
Lists in react class component:
We will be mostly focusing on lists of functional components in this article:
Lists in react functional component:
In the above example, we have called ListComponent and passed the myList array as the prop in render(). The ListComponent take mylist and maps every element of the array, and prints them in the form of a list. This code will generate the following output:
But if you will open the console in your browser, you will notice that react throws an error (warning):
This error is generated because react expects a unique key associated with each react list element. Keys are necessary for react app to boost performance.
Rendering Multiple Components
You can traverse through the elements of the array using the map() function and then return a list element <li> for each item. Then we can assign the resultant array of elements to listItems:
We can call the listItems inside a <ul> element:
The above code will display the following ouput:
Basic List Component
In general, we should refactor the code in the previous example. As you would render lists inside a component that accepts a collection of elements and outputs them in the list form.
But if you will open the console in your browser, you will notice that react throws an error (warning):
We can fix the error by passing the key prop to our map() function and fix the above warning:
Keys
Keys are required by react to uniquely identify every component of the list for optimal performance re-rendering. Keys help react pinpoint the item in a list that has been removed, changed, or updated.
How Does React Key Attribute Work?
Keys are always in string format and should be given to every element to give them a unique identity. In the case of the above example:
We should always choose a field that is unique for every react list element so that the key uniquely pinpoints a list item among its siblings. Like in the case of the database, we have a unique ID:
At last, we can use the index as a key, but it is not recommended as it hurts the performance of the react app, which will be discussed in the later sections of the article:
Extracting Components with Keys
Keys are only meaningful in the context of the surrounding collection. Let us see the incorrect and correct ways to extract an element from a list. Consider a case where you have a Component to return the different list items of a collection. In that case, you need to assign the key prop to that <Component /> and not the <li> inside that component.
Example: Incorrect Key Usage:
Example: Correct Key Usage:
Keys Must Only Be Unique among Siblings
It is a good practice to follow that a key for each item needs to be unique, by that is only true for the keys that are should only within an array. In simple terms, we can say that the keys in react should not be globally unique, but they should be unique among their siblings. Hence we are allowed by react to use the same keys in completely different and unrelated components.
Example:
Output:
The above output doesn't show any warnings or errors.
React doesn't pass the key to your component but only serves as a hint. If a unique value is required, we need to pass keys as a prop with a distinct prop name.
In the above example, the Post component can not read props. key but will read props. id.
Embedding map() in JSX
In the previous section, the JSX included a separate listItems variable.
We can also use the curly braces {} (embedding), to write the map() function inline:
How Do You Use Keys in Lists?
React uses the key attribute to pinpoint the uniqueness of an element among its siblings that is simple terms during re-renders and for neighboring elements of the same type, i.e. flat lists.
Let us try to understand the above statement using some coding examples:
Example without key prop:
Now using the keys example:
Explanation:
The above code is the refactored version. If you notice, we have changed the simple list into a list of id-value pairs. Now each item of the list is associated with its unique id. Hence we have used this unique id to be assigned as the key prop in the list for each item. This way of using a unique string for each of the list items is the most efficient way to way to assign keys in react.
Uniqueness of Keys
We have already discussed that keys should be unique among the siblings of an array, not unique globally. Thus if we have two arrays, such as in the example given below, we should have unique keys in each array, but the two collections of items can have an identical set of keys. This only means we are allowed by react to use the same keys in entirely different and unrelated components.
Example:
Output:
Explanation:
In the above example, we have made two different collections - itemSet1 and itemSet2. All the keys in collection one and collection two are unique among their respective siblings, but the keys are not globally unique. The two collections have the same keys for the first five elements, and the react runs the above code without throwing any error or warning.
Keys Don’t Automatically Get Passed as a Prop to the Component
React doesn't pass the key to your component but only serves as a hint. In simple it means, React does not pass the key as a prop automatically to a component. If a unique value is required, we need to pass keys as a prop with a distinct prop name. Let us take an example to understand better:
In the above example, the Post component can not read props. key but will read props. id.
Why Random “key” Attributes are a Bad Idea?
Let us understand why a random key is a bad idea with the help of an example. Suppose we have a Component known as CityList to render the list of cities.
Let us define the Item component that will render the information of the data of the city.
Now, if we don't supply the key attribute on the list items and execute the code, then:
React will start using the array's index as the key attribute, as there was no key specified, and the component will render the elements. It is similar to adding key={index} by reacting automatically to the Item component explicitly. Thus if the component re-renders, no item is changed but every Item in the list will re-render as well.
Note:
To prevent re-renders in the above example, to prevent re-renders we can make use of React.memo. This will prevent the re-renders if no changes have been made and improve the performance.
Now let us understand what will happen if we assign some random string to the key attribute:
Now following this will happen:
- React will re-generate the key attribute on every re-render.
- React will use the random key attribute to identify every existing element of the list.
- On re-render, all the key attributes will become some new random string, hence react will consider the old items as removed, and every item will be considered as new. React will mount the new items and unmount the old ones.
Re-mounting operation is a much more expensive problem than the re-render in terms of performance.
Advantages and Disadvantages of “index” as a “key” Attribute is Not a Good Idea
Disadvantages of “index” as a “key”
We can see the changes (bugs or performance) issue only when the items in the list change between re-renders, i.e. the list is dynamic in nature.
Let's take the previous example (of a city), and add sorting functionality.
The button is introduced to reverse the order of the array. Now we are going to use,
- key = city.id
- key = array’s index
For the performance factor, we are memorizing the Item component:
Now, if you click the button, you will notice that in index-based, every item is re-rendered even though only the order is changed (also, the Item component is memoized). Technically no re-render should happen.
Now in the case of key-based implementation, No item will re-render after clicking the button, and the console is clear.
Now let us understand the difference between the behavior of both components. However, it might be clear that the main difference is because of the key attribute.
- React tries to identify the elements to be re-rendered based on the before and after values of the key attribute.
- From the perspective of react, the same items are items that have the same value of the key attribute.
- In the index based implementation, the first item will have a 0 indexes the second will have a 1 index and so on, regardless of the order of array elements.
Hence in the index-based key implementation, when react pinpoints the before and after values of the key=0 in the list, react identifies both are the same item with different prop values, that is when the array is reversed only the city value is changed. Hence react will trigger the re-render cycle as react thinks the city props value is changed. Now react will bypass the memoization function and will trigger the re-render.
In the id-based implementation, items are identified as before, no component will re-render, and every item is memoized. Hence performance-wise, it is best.
To make the performance visible, we can change the color of the background (introduction of state) when the component re-renders.
Now the behavior of the index-based key is a little weird. If you click on the first item and then click on the sort button. React will think key=0 is identical to before, and only the props have been changed. Hence react will keep the state as it was and re-use the same component instance, i.e. state will be the same (active=true), and props will change (first city=last city). Whereas the id-based key behaves the same as before.
Advantages of “index” as a “key”
From the previous section, it is clear that using a unique id-based key is always a good idea. But now you know how to react and re-render the list component and using that knowledge, we can cheat and can optimize the performance even further using the index-based key instead of the id-based key.
A typical case when an index-based key is a good idea instead of an id-based key is a paginated list. Suppose you have a limited number of items to display in the list, and you want to show different items.
- Here if you will use the index-based key approach, all the items in the new page will already exist on the new page and will just change the props (that is, new data value). In this case, the component will not unmount. This will be very fast, even in the case of small data values.
- However, if you use the id-based key approach, every time you will load a new page, new data will be loaded with a completely new set of key values, and react will not be able to find any existing items, hence react will unmount the whole list component and mount the fresh list data value component.
Hence the re-render in the case of the index-based key will be much faster and optimized in terms of performance rather than the re-mounting of a component in the case of the id-based key.
Conclusion
- Keys are required by react to uniquely identify every component of the list for optimal performance re-rendering
- For extracting components, assign the key prop to that <Component /> and not the <li> inside that component.
- Keys must only be unique among siblings and not globally.
- React does not pass the key as a prop automatically to a component.
- Assigning random key attributes will reduce the performance.
- Using the index-based key is a good idea in case of pagination to enhance performance.
- It is always suited to use the unique id-based key in general.