12

I have been working on react for more than an year now. I have mostly played with iterating an array using .map, .forEach, .filter or using Object.keys and Object.values if it is an object.

But what are the different ways to add a unique key to jsx element. Below is what I have been used to till now

Using unique id from data as key to key prop:

const data= [{"id": "01", "name": "abc"}, {"id": "02", "name": "xyz"}];

render(){
  const items = data.map(item => {
    return <span key={item.id}>{item.name}</span>;
  }
  return(
     <div>
        {items}
     </div>
  )
}

Using index as key to key prop:

const data= [{"id": "01", "name": "abc"}, {"id": "02", "name": "xyz"}];

render(){
  const items = data.map((item, i) => {
    let keyValue = i+1;
    return <span key={keyValue}>{item.name}</span>;
  }
  return(
     <div>
        {items}
     </div>
  )
}

Is there any other ways to add a unique key to the jsx element apart from what I have mentioned above and which is most efficient and recommended?

5
  • 4
    It doesn't matter how you add them. What matters is that the actual content has some unique identifier in case the elements are rerendered in a different order. index is almost always incorrect because indices are always ordered numerically where data is practically guaranteed not to be. You can't just "add" a unique key to jsx and hope for the best. Unless your jsx content has literally no unique identifiers then use index since it won't matter to React's reconciliation algorithm because duplicate content is a known possibility. docs Commented Sep 6, 2018 at 3:47
  • Thank you. Is there any other way to add unique key if I don't have unique id in data and other than using index? Commented Sep 6, 2018 at 4:01
  • what for though? either the data is unique and it has something to uniquely identify it or it doesn't and the keys are essentially useless Commented Sep 6, 2018 at 4:18
  • We are directing to other topic. The intention of my question is to understand if there are any other ways to add unique key to jsx element apart from using id from data and index. Also you are saying Keys are useless but then why react shows us warning when we don't add key prop during an iteration? Commented Sep 6, 2018 at 4:28
  • What about a random number? Or a uuid generator? there are many ways of creating random (unique) values but they do not assist React's reconciliation algorithm in determining what content to preserve Commented Sep 6, 2018 at 4:39

4 Answers 4

20
+50

First of all, avoid using random keys.

There are a lot of ways to write keys, and some will perform better than others.

To understand how the keys we've chosen impacts on performance, it's necessary to understand React's Reconciliation Algorithm.

https://reactjs.org/docs/reconciliation.html

tl;dr Introduces a heuristic for comparing Virtual DOM trees to make this comparison O(n), with n the nodes of this VDOM tree. This heuristic can be split in these points:

  • Components of different type will create a new tree: This means that, while comparing the old tree with the new one, if the reconciler encounters that a node did change its type (e.g. <Button /> to <NotButton />), will cause our Button to be unmounted with its children as well, and NotButton to be mounted with its children, as well.
  • We can hint React on how instances are preserved on VDOM, by avoiding to recreate them. These hints can be provided by using keys.: After deciding if the instance in a node should be preserved (because its type remains the same), the reconciler will iterate on that node's children to compare them.

Supose now that we have this:

<div>
  <Button title="One" />
  <Button title="Two" />
</div>

And we'd like to add a Button to the DOM on the next render, say

<div>
  <Button title="Zero" />
  <Button title="One" />
  <Button title="Two" />
</div>

The algorithm will go as follows:

  • Compares <divs> in both VDOMs. Since these have the same type, we don't need to recreate the new tree. Props are the same so there are no changes to apply to the DOM at this point.
  • Button One compares against Zero. Reconciler detects that here was a props change, then updates the DOM with this title.
  • Button Two compares against One. Reconcilier also detects a props change here and uses the DOM to write this change.
  • Detects that a new Button is added as last child, so creates a new Button instance at VDOM and write this change at DOM.

Notice that these has many operations on the DOM, because it compared components by their index.

Now, we can fix this behavior by letting know to our reconciler that these instances should be reused. Now, let's have this:

<div>
  <Button title="One" key="One" />
  <Button title="Two" key="Two" />
</div>

And we'd like to add a Button to the DOM on the next render, say

<div>
  <Button title="Zero" key="Zero" />
  <Button title="One" key="One" />
  <Button title="Two" key="Two" />
</div>

The algorithm will go as follows:

  • Compares <divs> in both VDOMs. Since these have the same type, we don't need to recreate the new tree. Props are the same so there are no changes to apply to the DOM at this point.
  • Takes the first child of childrens. 'It's a Button', says the reconciler. 'And has a key' ('One'). Then, seeks for a children whose key is the same in the new children list. 'Oh, I encountered it!' but the reconciler realizes that there is no change on its props. Then, no DOM operations will be needed for this one.
  • The same scenario occurs with the second Button, it will compare by keys instead of by index. Realizes that it's the same instance and no props were changed, so React decides to not apply changes on the DOM.
  • For the Button with 'Zero' key, since there no exists a child with the same key, realizes that an instance should be created at VDOM, and this change should be written on DOM.

So, using keys by predictable contents helps the reconciler to perform less operations on the DOM. Healthy keys are those that can be inferred from the object that is being mapped, like a name, or an id or even an url if we are transforming urls to <imgs />.

What about key=index? Will have no effect, since by default, reconciler compares by position, i.e. its index.

These keys should be globally unique? Not necessarily. These should be unique among siblings, so reconciler can distinguish them while iterating by a node's children.

What about random keys? These should be avoided at all costs. If a key changes on every render, this will be keeping React destroying and creating instances on the VDOM (and hence, making extra writes on the DOM) since a component with a key wasn't found among the new children, but a new one with the same type.

If the render output is like

<div>
  <Button key={randomGenerator()} />
</div>

Then, each time render is executed (e.g. due a props/state change, or even if it's parent is being re-rendered and our shouldComponentUpdate returns true), a new randomGenerator() key will be generated. This will go like:

'Hey! I've found a Button with a F67BMkd== key, but none was found in the next one. I'll delete it.' 'Oh! I've encountered a Button with a SHDSA++5 key! Let's create a new one'.

Whenever the reconciler tells that an instance should be deleted and unmounted, its internal state will be lost; even if we mount it again. The instance at VDOM will not be preserved in this case.

The Button was the same, but the reconciler did a mess at DOM.

Hope it helps.

Sign up to request clarification or add additional context in comments.

Comments

4

The best way to pick a key is to use a string that uniquely identifies a list item among its siblings. Most often you would use IDs from your data as keys:

const todoItems = todos.map((todo) =>
  <li key={todo.id}>
    {todo.text}
  </li>
);

When you don’t have stable IDs for rendered items, you may use the item index as a key as a last resort:

const todoItems = todos.map((todo, index) =>
  // Only do this if items have no stable IDs
  <li key={index}>
    {todo.text}
  </li>
);

Also note:

Keys used within arrays should be unique among their siblings. However they don’t need to be globally unique.

However real answer to your question lives here: https://medium.com/@robinpokorny/index-as-a-key-is-an-anti-pattern-e0349aece318

There are many libraries that generate random unique ids such as shortid or uuid (which is the most popular one, just look at the number of downloads) or else just create your own function that generates random strings.

You can add them directly into object in array

const todos = [
  { 
    id: uuid(),
    text: 'foo',
  }
]

and iterate like so:

const todoItems = todos.map(({id, text}) =>
  <li key={id}>
    {text}
  </li>
);

1 Comment

I'm not sure that using a random key is the 'best' alternative, but I agree that it is a last resort. Take @Manuel V. Battan's examples and there may be situations where the buttons will be rerendered and therefore given completely new random keys. However, a better option than random is taking something unique from the content and using that as the key. You can also take a composite of content and then hash it as Benoit says - this is a good alternative solution. So best is unique ID from source, intermediate is [hash] from content, final is random generation.
-4

md5 sha1 or even sha256 on the content.

1 Comment

What is this for?
-5

You can use Date.now() with index, your code will be as Ex.

const data= [{"id": "01", "name": "abc"}, {"id": "02", "name": "xyz"}];

render(){
  const items = data.map((item, i) => {
    let keyValue = Date.now()+i;
    return <span key={keyValue}>{item.name}</span>;
  }
  return(
     <div>
        {items}
     </div>
  )
}

1 Comment

On every render you will have different keys so every child component will be re-rendered, this is bad because it is not necessary and will hurt performance. This could also mess up the state of the child components! Please don't do that :-)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.