There is also one more thing you need to remember about and that is the React Key attribute, each element of the rendered list must have it. This concept is one of the first things every front-end developer learns about React at the start of their journey. Now let’s dig a little bit deeper to explore why this is the case, and when we can take some shortcuts.
Why do we need this key attribute?
The simplest answer here would be “to optimize re-renders”, but the more complete one has to at least mention the concept of React Reconciliation. This is the process of figuring out how to update the UI in the most efficient way. To do that fast, React has to decide which parts of the tree of elements need to be updated and which do not. The thing is there might be a lot of elements in the DOM and comparing each of them in deciding which one should be updated is pretty expensive. To optimize this, React implements the diffing algorithm which is based on two assumptions:
- Two different types of elements will never be the same – so re-render those.
- Developers can manually help optimize that process via key attributes, so the elements, even if their order has changed, can be localized and compared faster.
Based on that, we can conclude that each React key should also be unique (within the list of elements, not globally), and stable (should not change). But what could happen when they are not tat?
Uniqueness, stability and array index
As we mentioned before, React keys should be stable and unique. The easiest way to achieve this is to use a unique ID (for example from a database) and pass it to each element when mapping an array, easy. But what about situations when we don’t have an ID, a name or other unique identifiers to pass to each element? Well, if we don’t pass anything as a key, React will take the current array index by default (since it is unique within that list) to handle it for us, but it will also give us a nice error message in the console:
Why is that the case? The answer is that the array index is not stable. If the order of the elements changes, each of the keys will change and we have a problem. If React encounters a situation where the order in a list of elements was changed, it will still try to compare them by the keys, that means that every comparison will end up in re-building a component and, as a result, the whole list will be rebuilt from scratch. In addition to that, we can encounter some unexpected problems, like component state updates for elements like uncontrolled inputs and other magical hard-to-debug problems.
Let’s get back to the array index. If using it as a React key can be so problematic, why React will use it by default? Is there any use-case where it’s okay to do so? The answer is yes, the use-case for that is static lists. If you’re sure that a list you’re rendering will never change its order, it's safe to use array index, but remember, if you have any unique identifiers, it's still better to use them instead. You can also notice that passing indexes as keys will make the React error message disappear, while simultaneously triggering some of the external linters to display an error or warning. This is due to the fact that the explicit usage of indexes as keys is considered a bad practice and some linters might treat it as an error, while React itself considers it as a “the developers know what they are doing” situation – so don’t do that unless you’re really know what you’re doing. A few examples of when it can be okay to use that exception would be a dropdown with a static list of buttons or displaying a list of elements with your company address information.
An alternative to a dataset-based key
Let’s say that none of the above is an option for us. For example, we have to display a list of elements based on the array of strings which can be duplicated, but it can also be reordered. In this case, we cannot use any of the strings because they are not unique (this can cause some magical bugs, too), and the array index is not good enough because we will change the order of elements. The last thing we can rely on in situations like this is the use of some external identifiers. Remember, it has to be stable so we can’t just go for Math.random(). There are some NPM packages that we can use in such cases, for example the “uuid” package. Tools like that can help us keep our list keys stable and unique, even when we cannot find proper identifiers within our data set. We should consider using database ID and array index (if possible) first, to minimize the number of packages used by our project.
To wrap it up
- Each element on the list of React elements should have a unique, stable key attribute,
- React keys are used to speed up the Reconciliation process and avoid unnecessary rebuilding of elements on the lists,
- The best source for keys is data entry unique ID (for example, from the database),
- You can use the array index as a key but only for a static list the order of which will not change,
- If there is no other way to get stable and unique keys, consider using some external ID providers, for example, the “uuid” package.
Why you should (probably) use Typescript
How not to kill a project with bad coding practices?
Data fetching strategies in NextJS