Key optimization in React

Key optimization in React

Introduction

When it comes to working with keys in React, this is what most developers know:

const List = () => {
  return items.map((item) => <ListItem key={item.id} {...item} />);
};

Yes, we should use keys so that React can efficiently update the list by keeping track of which items have changed, are added, or are removed.

However, there is another optimization you can do with the key prop.

Under the hood

To understand the optimization, let's dig into React's reconciliation process with keys:

// Scenario 1: No key changes
<div>
  <Component prop="A" /> // React sees: "Same component, same position, update props"
</div>

// Scenario 2: With different keys
<div>
  <Component key="1" prop="A" /> // Time 1
  <Component key="2" prop="A" /> // Time 2: React sees: "Different instance, destroy old, create new, I don't care about the props, everything is new."
</div>

React builds a tree of fiber nodes (internal representation).

During reconciliation, it first checks the key and type.

If key is different: React won't even attempt to reconcile/reuse. It will unmount (remove/destroy) old instance and mount new.

If key is same: Proceeds to check props for updates.

Animation example

Look at this:

<AnimatedNumber value={count} />

The first time it mounts, it will create a new instance of AnimatedNumber and the animation will run (think of CSS classes, animation runs the first time it's mounted).

To have this animation run every time count changes, we need to use the key prop:

<AnimatedNumber key={count} value={count} />

Example from personal experience

You have a set of tabs and in those tabs you have values that you can copy.

When clicking the copy button, it shows a checkmark for a second.

When switching between tabs, you do not want the checkmark to be shown. You should have a fully new copy button component within each tab.

How it may look like:

<Tabs>
  <TabItem label={tab1.label}>
    <CopyButton value={tab1.value} key={`copy-button-${tab1.label}`} />
  </TabItem>
  <TabItem label={tab2.label}>
    <CopyButton value={tab2.value} key={`copy-button-${tab2.label}`} />
  </TabItem>
</Tabs>

This is just a quick example. But it's good to know that keys can be used to uniquely identify the same component in different places in the tree.

Why is index not a good idea?

You have heard that using index as a key is not a good idea.

Here's why:

<div>
  <Component key={0} />
  <Component key={1} />
</div>

The reason this is not a good idea is when you reorder the items. This can happen during a render if the order of items initially is not the same every time e.g. sorted from the API or something.

Or it can happen when you've got a list that you can filter, drag reorder, etc. Otherwise it’s ok to use index as keys. But of course, it’s always recommended not to. Just more unnecessary cognitive effort to know when you really NEED the index versus when you don’t.