Home » Understanding unique keys for array children in React.js

Understanding unique keys for array children in React.js

Solutons:


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.

Try removing the key={i} from the <b></b> element inside the div’s (and check the console).

In the sample, if we don’t give a key to the <b> element and we want to update only the object.city, React needs to re-render the whole row vs just the element.

Here is the code:

var data = [{name:'Jhon', age:28, city:'HO'},
            {name:'Onhj', age:82, city:'HN'},
            {name:'Nohj', age:41, city:'IT'}
           ];

var Hello = React.createClass({
    
    render: function() {
            
      var _data = this.props.info;
      console.log(_data);
      return(
        <div>
            {_data.map(function(object, i){
               return <div className={"row"} key={i}> 
                          {[ object.name ,
                             // remove the key
                             <b className="fosfo" key={i}> {object.city} </b> , 
                             object.age
                          ]}
                      </div>; 
             })}
        </div>
       );
    }
});
 
React.render(<Hello info={data} />, document.body);

The answer posted by @Chris at the bottom goes into much more detail than this answer.

React documentation on the importance of keys in reconciliation: Keys

Be careful when iterating over arrays!!

It is a common misconception that using the index of the element in the array is an acceptable way of suppressing the error you are probably familiar with:

Each child in an array should have a unique "key" prop.

However, in many cases it is not! This is anti-pattern that can in some situations lead to unwanted behavior.

Understanding the key prop

React uses the key prop to understand the component-to-DOM Element relation, which is then used for the reconciliation process. It is therefore very important that the key always remains unique, otherwise there is a good chance React will mix up the elements and mutate the incorrect one. It is also important that these keys remain static throughout all re-renders in order to maintain best performance.

That being said, one does not always need to apply the above, provided it is known that the array is completely static. However, applying best practices is encouraged whenever possible.

A React developer said in this GitHub issue:

  • key is not really about performance, it’s more about identity (which in turn leads to better performance). randomly assigned and changing values are not identity
  • We can’t realistically provide keys [automatically] without knowing how your data is modeled. I would suggest maybe using some sort of hashing function if you don’t have ids
  • We already have internal keys when we use arrays, but they are the index in the array. When you insert a new element, those keys are wrong.

In short, a key should be:

  • Unique – A key cannot be identical to that of a sibling component.
  • Static – A key should not ever change between renders.

Using the key prop

As per the explanation above, carefully study the following samples and try to implement, when possible, the recommended approach.


Bad (Potentially)

<tbody>
    {rows.map((row, i) => {
        return <ObjectRow key={i} />;
    })}
</tbody>

This is arguably the most common mistake seen when iterating over an array in React. This approach isn’t technically “wrong”, it’s just… “dangerous” if you don’t know what you are doing. If you are iterating through a static array then this is a perfectly valid approach (e.g. an array of links in your navigation menu). However, if you are adding, removing, reordering or filtering items, then you need to be careful. Take a look at this detailed explanation in the official documentation.

class MyApp extends React.Component {
  constructor() {
    super();
    this.state = {
      arr: ["Item 1"]
    }
  }
  
  click = () => {
    this.setState({
      arr: ['Item ' + (this.state.arr.length+1)].concat(this.state.arr),
    });
  }
  
  render() {
    return(
      <div>
        <button onClick={this.click}>Add</button>
        <ul>
          {this.state.arr.map(
            (item, i) => <Item key={i} text={"Item " + i}>{item + " "}</Item>
          )}
        </ul>
      </div>
    );
  }
}

const Item = (props) => {
  return (
    <li>
      <label>{props.children}</label>
      <input value={props.text} />
    </li>
  );
}

ReactDOM.render(<MyApp />, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>

In this snippet we are using a non-static array and we are not restricting ourselves to using it as a stack. This is an unsafe approach (you’ll see why). Note how as we add items to the beginning of the array (basically unshift), the value for each <input> remains in place. Why? Because the key doesn’t uniquely identify each item.

In other words, at first Item 1 has key={0}. When we add the second item, the top item becomes Item 2, followed by Item 1 as the second item. However, now Item 1 has key={1} and not key={0} anymore. Instead, Item 2 now has key={0}!!

As such, React thinks the <input> elements have not changed, because the Item with key 0 is always at the top!

So why is this approach only sometimes bad?

This approach is only risky if the array is somehow filtered, rearranged, or items are added/removed. If it is always static, then it’s perfectly safe to use. For example, a navigation menu like ["Home", "Products", "Contact us"] can safely be iterated through with this method because you’ll probably never add new links or rearrange them.

In short, here’s when you can safely use the index as key:

  • The array is static and will never change.
  • The array is never filtered (display a subset of the array).
  • The array is never reordered.
  • The array is used as a stack or LIFO (last in, first out). In other words, adding can only be done at the end of the array (i.e push), and only the last item can ever be removed (i.e pop).

Had we instead, in the snippet above, pushed the added item to the end of the array, the order for each existing item would always be correct.


Very bad

<tbody>
    {rows.map((row) => {
        return <ObjectRow key={Math.random()} />;
    })}
</tbody>

While this approach will probably guarantee uniqueness of the keys, it will always force react to re-render each item in the list, even when this is not required. This a very bad solution as it greatly impacts performance. Not to mention that one cannot exclude the possibility of a key collision in the event that Math.random() produces the same number twice.

Unstable keys (like those produced by Math.random()) will cause many component instances and DOM nodes to be unnecessarily recreated, which can cause performance degradation and lost state in child components.


Very good

<tbody>
    {rows.map((row) => {
        return <ObjectRow key={row.uniqueId} />;
    })}
</tbody>

This is arguably the best approach because it uses a property that is unique for each item in the dataset. For example, if rows contains data fetched from a database, one could use the table’s Primary Key (which typically is an auto-incrementing number).

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


Good

componentWillMount() {
  let rows = this.props.rows.map(item => { 
    return {uid: SomeLibrary.generateUniqueID(), value: item};
  });
}

...

<tbody>
    {rows.map((row) => {
        return <ObjectRow key={row.uid} />;
    })}
</tbody>

This is also a good approach. If your dataset does not contain any data that guarantees uniqueness (e.g. an array of arbitrary numbers), there is a chance of a key collision. In such cases, it is best to manually generate a unique identifier for each item in the dataset before iterating over it. Preferably when mounting the component or when the dataset is received (e.g. from props or from an async API call), in order to do this only once, and not each time the component re-renders. There are already a handful of libraries out there that can provide you such keys. Here is one example: react-key-index.

This may or not help someone, but it might be a quick reference. This is also similar to all the answers presented above.

I have a lot of locations that generate list using the structure below:

return (
    {myList.map(item => (
       <>
          <div class="some class"> 
             {item.someProperty} 
              ....
          </div>
       </>
     )}
 )
         

After a little trial and error (and some frustrations), adding a key property to the outermost block resolved it. Also, note that the <> tag is now replaced with the <div> tag now.

return (
  
    {myList.map((item, index) => (
       <div key={index}>
          <div class="some class"> 
             {item.someProperty} 
              ....
          </div>
       </div>
     )}
 )

Of course, I’ve been naively using the iterating index (index) to populate the key value in the above example. Ideally, you’d use something which is unique to the list item.

Related Solutions

Why would anyone choose not to use the lowlatency kernel?

The different configurations, “generic”, “lowlatency” (as configured in Ubuntu), and RT (“real-time”), are all about balancing throughput versus latency. Generic kernels favour throughput over latency, the others favour latency over throughput. Thus users who...

How can I update all Snap packages?

sudo snap refresh Will do this. It is part of snapd 2.0.8, which landed 2016-06-13 in xenial-updates. snap refresh --list Only lists the updates without refreshing the packages. snap info <snap name> Can show which versions are available for a particular...

What does Controls.Add() do in c#? [closed]

Controls is an instance of Control.ControlCollection class, which represents a collection of Control objects, Inheritance hierarchy is System.Windows.Forms.Control.ControlCollection Note: The Add, Remove, and RemoveAt methods enable you to add and remove...

How can I change the date modified/created of a file?

As long as you are the owner of the file (or root), you can change the modification time of a file using the touch command: touch filename By default this will set the file's modification time to the current time, but there are a number of flags, such as the -d...

How to read dmesg from previous session? (dmesg.0)

Although a bit late for the OP... I use Fedora, but if your system uses journalctl then you can easily get the kernel messages (dmesg log) from prior shutdown/crash (in a dmesg -T format) through the following. Options: -k (dmesg) -b < boot_number > (How...

Get data on daily basis in laravel

You can use the existing Laravel cron job scheduling to fulfill your specific requirement. Please refer following Laravels official documentation https://laravel.com/docs/5.8/scheduling#scheduling-queued-jobs I'll show a simple example to get an idea about this...

Python speed testing – Time Difference – milliseconds

datetime.timedelta is just the difference between two datetimes ... so it's like a period of time, in days / seconds / microseconds >>> import datetime >>> a = datetime.datetime.now() >>> b = datetime.datetime.now() >>> c = b...

Discovering metadata about a PDF

One of the canonical tools for this is pdfinfo, which comes with xpdf, if I recall. Example output: [0 1017 17:10:17] ~/temp % pdfinfo test.pdf Creator: TeX Producer: pdfTeX-1.40.14 CreationDate: Sun May 18 09:53:06 2014 ModDate: Sun May 18 09:53:06 2014...

How can I search within the output buffer of a tmux shell?

copy mode search To search in the tmux history buffer for the current window, press Ctrl-b [ to enter copy mode. If you're using emacs key bindings (the default), press Ctrl-s then type the string to search for and press Enter. Press n to search for the same...

Often big numbers become negative

This image shows what you're looking for. In your case it's obviously larger numbers, but the principle stays the same. Examples of limits in java are: int: −2,147,483,648 to 2,147,483,647. long: -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 In the...

null pointer exception in java servlet [closed]

I got a "null pointer exception" fault in java servlet. Could someone tell me what happens? And how to avoid that? That happens when you're trying to access/invoke some reference which is actually null. SomeObject someObject = null; someObject.doSomething(); //...

How to set ulimits on service with systemd?

The mappings of systemd limits to ulimit Directive ulimit equivalent Unit LimitCPU= ulimit -t Seconds LimitFSIZE= ulimit -f Bytes LimitDATA= ulimit -d Bytes LimitSTACK= ulimit -s Bytes LimitCORE= ulimit -c Bytes LimitRSS= ulimit -m Bytes LimitNOFILE= ulimit -n...

Does compression option -z with rsync speed up backup

It's a general question. Does compression and decompression at endpoints improve the effective bandwidth of a link? The effective (perceived) bandwith of a link doing compression and decompression at endpoints is a function of: how fast you can compress (your...

How to pre-download items from a JSON list array in React JS?

You can insert <link rel="prefetch"> elements into the <head> of the page. This will tell the browser to go ahead and download the thing that it finds in the src property of that element so that it can be served from the cache if something else on...

C programming: Use of undeclared identifier [closed]

There are a lot of error in this program. In c programming, before using a variable, we must explicitly declare the type of data that it can store. So you must define the type of x and y to integer type (int x = 10,int y=12). The next thing is that you are...