Everyone dealing with React knows about this warning: Warning: Each child in a list should have a unique âkeyâ prop. It shows up in your development tools of your browser and itâs one of the warnings you encounter very early in your React career. The following list component results in this warning:
The warning says we only need to add a key attribute to each of our list item elements. Since we are using the built-in JavaScript array map method , we have access to the index of each rendered item in the list. That should do the trick, shouldnât it?
Indeed, the warning disappears and we should be alright for now. But careful: Using the index of an array item isnât the best practice solution here. React asks for the key not without any reason. Behind the scenes, React uses the key attribute to associate the rendered element with its place in the rendered list. Basically itâs the identifier for the item element in the list element.
For instance, the solution with the index would fail in the case of reordering the list, because the index would stay the same but the real item has changed its place. If we would reverse our array, the 'Learn React' entry would have the index 2 and the 'Learn GraphQL' entry would have the index 1 . That doesnât match anymore with the actual rendered items which causes problems.
When I searched online about this problem, it was difficult to find a real world scenario that illustrates it. Most tutorials only explain how to fix the issue, but not what could happen in the worst case. Thatâs why I came up with the following example which is perfect for a brief walkthrough to show the issue:
The example showcases the same list, but this time managed with React Hooks as state . The new button element reverses our list and stores it as state. If you try the example, everything works and seems alright. The bug stays hidden because we donât render much here. However, if we add another uncontrolled element to our rendered list items, we can see the bug happening:
The checkbox â since itâs an uncontrolled element for the sake of demonstrating whatâs happening here â manages its own internal state. If you check the first of the two items with the checkbox, and reverse them with the button, you will notice that the checked checkbox is rendered at the same place while the order of the list items have changed.
Now the flaw â which wasnât obvious before â got unveiled in our browser in front of our eyes. The problem is that we are using the index of each item in an array for the key attribute. But for each rendering the index stays the same even though we reverse our list:
Thatâs why the reordered elements get still assigned to the same key attribute which acts as identifier for React. React doesnât change the checkbox elements, because it believes the rendered items still have the same order. You can fix this issue by using stable identifiers for your rendered list items:
Now with every reorder of the list, the key property stays intact because the id is attached to the actual item in the list:
Thatâs all it needs to render lists without any bugs in React. In this case we used a made up identifier, but it would have worked with the name property of each item as key as well; as long as they are unique names which cannot be changed.
Anyway, itâs still worth to note that using indexes is fine as long your list keeps isnât changed in order or size. Then the place of each item in the list doesnât change â itâs stable same as the index â and thus using the index is okay. The demonstrated example can be found in this GitHub repository among other React List Component examples.