Dynamic Key Generation in React

Published on
5 mins read
––– views

What are React keys?

React is pretty good at handling its own state management. But when we use the .map() function to render an array of elements, React doesn't really know much about those elements.

That's where keys come in. They're like passports that identify each rendered element in the list. When we use keys, React can keep track of which elements have changed and which haven't, and it can optimize rendering performance by only updating what's necessary.

Think about it this way: you're boarding a plane, and your ticket, seat, and passport are all linked together. Now imagine that one passenger decides not to board the plane.

The plane has 500 seats, and it's fully booked, but the passenger who was supposed to sit in seat number 456 decides not to come. In real life, what happens? Nothing much, really. Everyone else just stays in their seat and waits for takeoff.

But in React's world, if we don't use keys, it would be like everyone on the plane leaving, getting their current tickets cancelled, giving them new tickets, getting their passport and new ticket checked all over again, and then getting re-seated.

That's because without keys, when a change occurs, React doesn't know which elements in the list have changed, so it has to re-render them all. This can hurt performance.

While it's technically possible to render components without keys, but doing things just because they are possible may not be the best approach.

Finding the key

Usually, data comes with an unique ID that we can link our rendered component to. There are also times, it does not.

const passengers = [
  {
    name: 'Bob Bobber',
    passportId: 'ABC123',
  },
  {
    name: 'Kitty Cat',
    passportId: 'DEF456',
  },
  {
    name: 'Bob Bobilliybob',
    passportId: 'GHI789',
  },
]

// We can use the `passportId` field in our iterations:
passengers.map((passenger) => <Passenger key={passenger.passportId} name={passenger.name} />)

In this example, we have passengers' name and their passportId. Names won't be unique, as many people may share the same last name and first name.

Passport ID, on the other hand, is unique. So we could use that property as our 'key'. Sometimes you don't need to have a specified id or key in your data. You can get creative, as in this example.

Some data comes with a built-in 'id' property. If it exists, make sure each id is unique and use it as the key.

Using index as React Key's

You may think that using 'index' would solve this problem. However, 'index' is assigned per position, not per item. Therefore, using 'index' can cause problems or offer no benefits if the position of the item changes.

For example, imagine this dataset:

item1 - index 0
item2 - index 1
item3 - index 2
item4 - index 3
... - ...
... - ...
item200 - index 199

What would happen if the item at index 3 is removed?

  • The items from index 0 to 2 will keep their index and won't trigger a re-render.
  • The items from index 3 to 199 will receive new index values, which will trigger a re-render.

By removing a single component from the list, we have to re-render +190 components. This is because index values are dynamic and change.

Math.random() as keys?

That would work if implemented correctly, and I will show you what I mean.

This code will generate quotes from a source. Take a look at it, and think about it for a bit. Do you think this would work or not?

When the user clicks on the button, the app will generate a new quote and display it. Don't worry about all the details or how this code would work. The main point of this example is key={}.

;<button
  onClick={(event) => {
    const randomIndex = Math.floor(Math.random() * quotes.length)
    const randomQuote = quotes[randomIndex]
    console.log(randomQuote)
  }}
>
  Generate Quote
</button>

{
  // pay attention here --------
  quotes.map((quote) => <p key={Math.random()}>{quote}</p>)
}

The problem with the example is that every time the button is clicked, the key of the previous quote changes. Although we are assigning keys, they are not useful and provide no benefit. This is similar to using index as the key, which we discovered being not very useful.

So, we need to create an id and the quote together, link them to each other and pass that to the .map function.

  <button
        onClick={() => {
            // grabs random quote from data
          const randomIndex = Math.floor(Math.random() * data.length);

          const newQuote = {
        // random quote is selected
            text: data[randomIndex],
        // key value generated for that quote.
            id: crypto.randomUUID(),
          };
          // new quote is set with useState()
          setQuote(newQuote);
        }}
      >

   {
quotes.map((quote) => <p key={quote.id}>{quote.text}</p>)
}

With the updated onClick function, the generated quotes will now have a unique id value which is created using crypto.randomUUID(). Alternatively, we could use Math.random() too.

In short:

  • Your components should have keys
  • Try not to use index values as keys. Can be OK for static data, but avoid it if you can.
  • If data has no key/id, generate them away from the .map function.
  • Generate the data and keys together, link them and pass to .map function.