Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't ReactJS autogenerate keys for dynamic children?

When coding ReactJS I have to supply keys for dynamic children. For example:

render() {
  const {options} = this.state
  const availableOptions = options.map(opt => {
    return (
      <option key={opt.id} value={opt.id}>{opt.displayValue}</option>}
    )
  }

  return (
    <select onChange={this._onOptionSelect}>
      {availableOptions}
    </select>
  )
}

I understand why they keys are there. But why do I have to give them? Couldn't react just assign a running number or UUIDv4 or something else?

Related doc: http://facebook.github.io/react/docs/multiple-components.html#dynamic-children

like image 675
elnygren Avatar asked Feb 11 '16 12:02

elnygren


People also ask

How do I add a unique key prop in React?

You should add a key to each child as well as each element inside children. This way React can handle the minimal DOM change. In your code, each <TableRowItem key={item.id} data={item} columns={columnNames}/> is trying to render some children inside them without a key. Check this example.

Why React need a key prop?

React's key prop gives you the ability to control component instances. Each time React renders your components, it's calling your functions to retrieve the new React elements that it uses to update the DOM. If you return the same element types, it keeps those components/DOM nodes around, even if all the props changed.

How do you resolve warning each child in a list should have a unique key prop?

The easy fix is to assign the “key” prop to the element like so. This will resolve the warning that was occurring previously since each child will now have a unique key.

Why do we pass key prop while displaying an array of elements?

Keys help React identify which items have changed, are added, or are removed. Keys should be given to the elements inside the array to give the elements a stable identity. When the state of your component changes, the render function will return a new tree of React elements, different to the previous/current one.


2 Answers

Tl;dr

You need to assign a unique key to dynamic elements that are associated with that element's data (maybe an id from a database field or something) because it stops unnecessary re-renders. This is the main draw of React and why it is noted for its performance.

Reason

You need to assign a unique key to dynamic children because this is how React's virtual DOM associates that element with a specific piece of data. I think an example would help illustrate.

Let's say you have a list of 1,000 dynamically generated items. You could just use the index parameter that is passed in from the map function to dynamically assign a key to those items. However, what if you wanted to change the order of those items - maybe sort them alphabetically? Because the key on those items is not tied to the specific piece of data and is instead just generated dynamically, the React virtual DOM has no way to keep track of those elements. That means it would have to re-render all 1,000 elements just to change the sorting. However, let's say each of those items had a unique id assigned to it that was populated from the database. The virtual DOM is smart enough to see that even though the order of the elements has changed, the data in the element itself is still the same. Therefore, it would re-render none of the elements, despite the fact that their order was changed.

If any of that is unclear, it makes total sense once you dissect how the virtual DOM really works. Essentially the virtual DOM is a copy of the actual DOM. React compares the two and only re-renders what has actually changed. This is where React gets its speed. So let's say you have a list of 3 dynamic <Item /> components and you are also generating their key dynamically.

<Item key="1">Banana</Item>
<Item key="2">Orange</Item>
<Item key="3">Apple</Item>

Now if you reorder those items alphabetically, their keys will also get reassigned dynamically.

<Item key="1">Apple</Item>
<Item key="2">Banana</Item>
<Item key="3">Orange</Item>

At this point React compares the contents of key 1 and sees if it has changed from the previous render of key 1. It has so it completely re-renders that element. Then it checks for key 2. Its contents have changed also, so that gets re-rendered. This continues for the whole list.

Now imagine that each item has a unique ID that is associated with it in the database and you assign this as the key.

<Item key="782364">Banana</Item>
<Item key="434533">Orange</Item>
<Item key="834535">Apple</Item>

Now we reorder that list alphabetically:

<Item key="834535">Apple</Item>
<Item key="782364">Banana</Item>
<Item key="434533">Orange</Item>

At this point React would check to see if the contents of the item with key 834535 are still the same. Well, the contents are still the same! So while that element gets a different order, it is not re-rendered. Then it checks for the element with key 782364 and finds that its contents are also the same. This also continues for the whole list.

While in a small list, you probably wouldn't notice the difference between a dynamically generated key vs. one that is tied directly to that element's data, for large lists the performance benefit is huge. And that really is the main draw of React - very smart re-rendering.

like image 165
Andy Noelker Avatar answered Oct 17 '22 01:10

Andy Noelker


Why doesn't ReactJS autogenerate keys for dynamic children?

It does, because it needs something to identifiy the elements in the virtual dom tree. If you inspect the generated html for some dynamic children with the key prop omitted you'll see:

enter image description here

Where key ∈ {0,1,2,3}

But that is unspecified behaviour and you should not rely on that. The warning is there because:

  • React does not and does want to make any assumptions about your application. It is the responsibilty of the developer to supply a suitable key, so that all the react specific optimizations (e.g. reuse of dom nodes) can be applied.
  • They (Facebook) can change the unspecified default behaviour at any time and thus break any code that relies on that.

It cannot be repeated often enough: Don't rely on the default behaviour!

like image 43
fl0cke Avatar answered Oct 17 '22 00:10

fl0cke