13

I have a list of items that doesn't contain enough data to generate a unique key. If I use the uuid library to generate an ID, will a single item change also causes the other items to re-render since their key will change each time?

const people = [
  {
    gender: 'male',
    firstName: 'david',
  },
  {
    gender: 'male',
    firstName: 'david',
  },
  {
    gender: 'male',
    firstName: 'joe',
  },
]

const renderPeople = () => {
  return people.map(person => {
    return (
      <div key={uuid.v4() /* a new value each time? */ }>
        <p>{person.gender}</p>
        <p>{person.firstName}</p>
      </div>
    )
  })
}

some time later... one of the davids changed

const people = [
  {
    gender: 'male',
    firstName: 'david',
  },
  {
    gender: 'male',
    firstName: 'davidzz',
  },
  {
    gender: 'male',
    firstName: 'joe',
  },
]
Alireza
  • 2,319
  • 2
  • 23
  • 35
NameTaken
  • 527
  • 1
  • 5
  • 16
  • Possible duplicate of [When giving unique keys to components, is it okay to use Math.random() for generating those keys?](https://stackoverflow.com/questions/29808636/when-giving-unique-keys-to-components-is-it-okay-to-use-math-random-for-gener) – devserkan Oct 08 '18 at 15:22
  • @devserkan I don't think that thread fully solves my problem as generating a UUID at fetch time will still creates new UUIDs for the same data. – NameTaken Oct 08 '18 at 15:31
  • You are right but as you can see there aren't much more options. We can't talk about different options here I think, all the suggestions will be so similar (This is why I voted this question as a duplicate). Either you will create a unique id with some properties (you don't have much here) or you will use the index if the order won't change or you will give to the objects some unique id in the creation time (if you are the one creating them). Other than that, if there will be any different options I also want to hear. – devserkan Oct 08 '18 at 15:39
  • 1
    @devserkan Thanks for the detail explanation and the other link. Yes there is always a possibility that there is no good solution, just want to check with the SO community before I commit :) – NameTaken Oct 08 '18 at 16:18

3 Answers3

25

<div key={uuid.v4()}> assigns new key for each <div> every time, so it is useless.

If the array stays same, UUID should be generated on array creation.

If the array changes, e.g. received from HTTP request, UUIDs for elements with same content will be generated again.

In order to avoid that, key should be specific to person entity. It's always preferable to use internal identifiers (database IDs) where available for unambiguity.

If identifiers are not available, key may depend on element contents:

return (
  <div key={JSON.stringify(person)}>
    <p>{person.gender}</p>
    <p>{person.firstName}</p>
  </div>
)

It's more efficient to hash elements once at the time when an array is created, e.g. with uuid:

import uuidv3 from 'uuid/v3';
...

for (const person of people) {
  person.key = uuidv3(JSON.stringify(person), uuidv3.URL);
}

Or use dedicated hashing function like object-hash.

Notice that hashing may result in key collisions if there are elements with same contents.

Estus Flask
  • 206,104
  • 70
  • 425
  • 565
4

Yes. Keys should be stable. If they're not, React can't infer that these items might be the same at all, and has to fully rerender them.

AKX
  • 152,115
  • 15
  • 115
  • 172
1

As answered before, keys must be stable. To achieve this, I have a suggestion for you.

I find it very simple and convenient to use UUIDs for keys. I use them as singletons and create them with a hook.

export function useSingleton<T>(provider: () => T): T {
    const singleton = useRef<T>();

    function getSingleton() {
        if (singleton.current == null) {
            singleton.current = provider();
        }
        return singleton.current;
    }

    return getSingleton();
}

export function useComponentId(): string {
    return useSingleton(uuidv4);
}

And use it like

const componentId = useComponentId();
...
something.map((thing, index) =>
    <Component key={`${componentId}.label.${index}`}/>
);

This is just a usage example though. I wouldn't say you NEED a UUID for exactly that case. e.g. It is very handy, when you don't have unique IDs on your 'thing' object. And it is much faster than calculating hashes or do JSON.stringify(). There is no danger for collisions either.

Mario Eis
  • 2,724
  • 31
  • 32