understanding unique keys for array children in react.js

  • Last Update :
  • Techknowledgy :

12 Your rows in JS array should have unique key property. It'll help ReactJS to find references to the appropriate DOM nodes and update only content inside mark-up but not re-render the whole table/row. – Kiril Feb 4, 2015 at 19:09 In ReactJS if you are rendering an array of elements you should have a unique key for each those elements. Normally those kinda situations are creating a list. Here are the React docs that explain well using the Key property, the key should be defined at the parent component it should not be used inside the child component.React Docs

This is our Splunktool team suggestion, we tried and its working fine ✌
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);
​

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);

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.
5._
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"));
6._
<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>

Suggestion : 2

When creating a list in the UI from an array with JSX, you should add a key prop to each child and to any of its’ children. It is not recommended to use the index of the array as the key prop if you know the array will not be static. If the key is an index, reordering an item in the array changes it. Then React will get confused and re-render the incorrect element. React uses the key prop create a relationship between the component and the DOM element. The library uses this relationship to determine whether or not the component should be re-rendered.

1._
Warning: Each child in a list should have a unique "key"
prop
2._
<ul>
  {["Item1", "Item2", "Item3"].map(item =>
  <li>{item}</li>
  )}
</ul>

Keys do not have to be unique globally. They just need to be unique across sibling elements.

<ul>
  {["Item1", "Item2", "Item3"].map(item =>
  <li key="{item}">{item}</li>
  )}
</ul>

Suggestion : 3

Below, we loop through the numbers array using the JavaScript map() function. We return a <li> element for each item. Finally, we assign the resulting array of elements to listItems: For example, if you extract a ListItem component, you should keep the key on the <ListItem /> elements in the array rather than on the <li> element in the ListItem itself. When you run this code, you’ll be given a warning that a key should be provided for list items. A “key” is a special string attribute you need to include when creating lists of elements. We’ll discuss why it’s important in the next section.

1._
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map((number) => number * 2);
console.log(doubled);
2._
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>  <li>{number}</li>);
3._
<ul>{listItems}</ul>
5._
function NumberList(props) {
  const numbers = props.numbers;
  const listItems = numbers.map((number) =>
    <li key={number.toString()}>      {number}
    </li>
  );
  return (
    <ul>{listItems}</ul>
  );
}
6._
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
  <li key={number.toString()}>    {number}
  </li>
);

Suggestion : 4

This is because React uses a unique “key” prop on each child of the list to create a relationship between the component and the DOM. This is to ensure that react re-renders the child correctly next time. And so, it’s important to have a unique key assigned to every child of the list. When rendering the list through the React.Children.toArray method, React will return the children opaque data structure as a flat array with keys assigned to each child. But there’s a more elegant and recommended way to fix this and this is to use React.Children.toArray method.

1._
export default function App() {
  const list = [
    { key: 1, value: 10 },
    { key: 2, value: 20 },
    { key: 3, value: 30 }
  ];

  return (
    <div>
    {list.map(({ key, value }) => (
        <div>{value}</div>
    ))}
    </div>
  );
}
2._
{list.map(({ key, value }) => (
    <div key={key}>{value}</div>
))}
3._
export default function App() {
  const list = [
    { key: 1, value: 10 },
    { key: 2, value: 20 },
    { key: 3, value: 30 }
  ];

  return (
    <div>
    {
        React.Children.toArray(
            list.map(({ key, value }) => <div>{value}</div>)
        )
    }
    </div>
  );
}

Suggestion : 5

The "Each child in a list should have a unique "key" prop." warning happens in React when you create a list of elements without the special key attribute. Keys must be assigned to each element in a loop to give stable identity to elements in React. Using array indexes for keys is a great way to give each element in a list a unique key if you don't have an id property on your data. However, if the order of the elements can be changed, this can cause issues in React. The key is used to correctly link the component to the DOM element. Keys must also be unique across siblings. The most common way to use unique IDs is to use either an id property on the passed object or use the index of the array.

1._
// ❌ Invalid, will cause the above error due to missing "key" prop
{posts.map(post => <Post details={post} />)}

// ✔️ Each element now has a unique key
{posts.map(post => <Post details={post} key={post.id} />)}

// ✔️ You can also use the index of the array
{posts.map((post, index) => <Post details={post} key={index} />)}
2._
// ❌ Don't use Math.random for a key
{posts.map(post => <Post details={post} key={Math.random()} />)}
3._
// ❌ This will not work if the array can be sorted or filtered
{posts.map((post, index) => <Post details={post} key={index} />)}

// ✔️ You must use a unqie ID in this case
{posts.map(post => <Post details={post} key={post.id} />)}

// ✔️ You can also use the uuid package
import { v4 } from 'uuid'

{posts.map(post => <Post details={post} key={v4()} />)}

Suggestion : 6

React.Children.toArray returns the children opaque data structure as a flat array with keys assigned to each child. Useful if you want to manipulate collections of children in your render methods, especially if you want to reorder or slice children before passing it down. Returns the children opaque data structure as a flat array. If there’s an absolute need that children should always be an array, you can use React.Children.toArray(children) instead. It’ll work perfectly even when children is an object or a function too.

In this article, we’ll look at a React utility React.Children.toArray which lets us prepare the children prop for inspection and iteration, some of its shortcomings and how to overcome them — through a small open-source package, to keep our React code function the way it is deterministically supposed to behave, keeping performance intact. If you know the basics of React and have at least an idea about what the children prop in React is, this article is for you.

While working with React, most of the time we do not touch the children prop any more than using it in React components directly.

function Parent({ children }) {
  return <div className="mt-10">{children}</div>;
}

But sometimes we have to iterate over the children prop so that we can enhance or change the children without having the user of the components explicitly do it themselves. One common use case is to pass the iteration index-related information to child components of a parent like so:

import { Children, cloneElement } from "react";

function Breadcrumbs({ children }) {
  const arrayChildren = Children.toArray(children);

  return (
    <ul
      style={{
        listStyle: "none",
        display: "flex",
      }}
    >
      {Children.map(arrayChildren, (child, index) => {
        const isLast = index === arrayChildren.length - 1;

        if (! isLast && ! child.props.link ) {
          throw new Error(
            `BreadcrumbItem child no. ${index + 1}
            should be passed a 'link' prop`
          )
        } 

        return (
          <>
            {child.props.link ? (
              <a
                href={child.props.link}
                style={{
                  display: "inline-block",
                  textDecoration: "none",
                }}
              >
                <div style={{ marginRight: "5px" }}>
                  {cloneElement(child, {
                    isLast,
                  })}
                </div>
              </a>
            ) : (
              <div style={{ marginRight: "5px" }}>
                {cloneElement(child, {
                  isLast,
                })}
              </div>
            )}
            {!isLast && (
              <div style={{ marginRight: "5px" }}>
                >
              </div>
            )}
          </>
        );
      })}
    </ul>
  );
}

function BreadcrumbItem({ isLast, children }) {
  return (
    <li
      style={{
        color: isLast ? "black" : "blue",
      }}
    >
      {children}
    </li>
  );
}

export default function App() {
  return (
    <Breadcrumbs>
      <BreadcrumbItem
        link="https://goibibo.com/"
      >
        Goibibo
      </BreadcrumbItem>

      <BreadcrumbItem
        link="https://goibibo.com/hotels/"
      >
        Hotels
      </BreadcrumbItem>

      <BreadcrumbItem>
       A Fancy Hotel Name
      </BreadcrumbItem>
    </Breadcrumbs>
  );
}

The user of Breadcrumbs and BreadcrumbItem doesn’t have to worry about which children should have links and how they should be styled. Inside the Breadcrumbs component, it automatically gets handled.

This pattern of implicitly passing in props and/or having state in the parent and passing the state and state changers down to the children as props is called the compound component pattern. You might be familiar with this pattern from React Router’s Switch component, which takes Route components as its children:

// example from react router docs
// https://reactrouter.com/web/api/Switch

import { Route, Switch } from "react-router";

let routes = (
  <Switch>
    <Route exact path="/">
      <Home />
    </Route>
    <Route path="/about">
      <About />
    </Route>
    <Route path="/:user">
      <User />
    </Route>
    <Route>
      <NoMatch />
    </Route>
  </Switch>
);

If you open up the console, you’d be able to see the difference.

[
   Object1, -- -- > first anchor tag
   Object2, -- -- > second anchor tag[
      Object3, -- -- > first fruit Object4, -- -- > second fruit Object5]-- -- > third fruit
]]
6._
[
   Object1, -- -- > first anchor tag
   Object2, -- -- > second anchor tag
   Object3, -- -- > first fruit
   Object4, -- -- > second fruit
   Object5, -- -- > third fruit
]

Suggestion : 7

The key issue can be solved by adding a key attribute to each Array components. Here is an example that renders 3 components from an array. You have an array of React components. You are trying to render it using a loop. While running the application, everything looks good. But in the browser console you see this error: React can render above component. No issue. But if we swap the Address and PIN component, React needs to re-render the entire array. If the key attribute is there, React will keep a record of that. Then it can swap Address and PIN without re-rendering Student component.

1._
VM9: 85 Warning: Each child in a list should have a unique "key"
prop.

Check the render method of `App`.See https: //reactjs.org/link/warning-keys for more information.
2._
[
<Student key="1" />,
<Address key="2" />,
<PIN key="3" />];
3._
[
<Student />,
<Address />,
<PIN />];

Suggestion : 8

In the previous article on ReactJS | Lists, we had discussed Keys and also told why they are needed while creating lists. We will continue the discussion further in this article. You can see in the above output that the list is rendered successfully but a warning is thrown to the console that the elements inside the iterator are not assigned keys. This is because we had not assigned key to the elements we are returning to the map() iterator. You can also assign the array indexes as keys to the list items. The below example assigns array indexes as key to the elements. 

A “key” is a special string attribute you need to include when creating lists of elements in React. Keys are used in React to identify which items in the list are changed, updated, or deleted. In other words, we can say that keys are used to give an identity to the elements in the lists. The next thing that comes to mind is that what should be good to be chosen as key for the items in lists. It is recommended to use a string as a key that uniquely identifies the items in the list. Below example shows a list with string keys:  

const numbers = [ 1, 2, 3, 4, 5 ];
 
const updatedNums = numbers.map((number)=>{
return <li key={index}>{number} </li>;
});
2._
const numbers = [ 1, 2, 3, 4, 5 ];
 
const updatedNums = numbers.map((number, index)=>
<li key = {index}>
{number}
</li>
);

Consider a situation where you have created a separate component for list items and you are extracting list items from that component. In that case, you will have to assign keys to the component you are returning from the iterator and not to the list items. That is you should assign keys to <Component /> and not to <li> A good practice to avoid mistakes is to keep in mind that anything you are returning from inside of the map() function is needed to be assigned key. 

Below code shows incorrect usage of keys: 

import React from 'react';
import ReactDOM from 'react-dom';
// Component to be extracted
function MenuItems(props)
{
const item = props.item;
return(
<li key = {item.toString()}>
{item}
</li>
);
}
            
// Component that will return an
// unordered list
function Navmenu(props)
{
const list = props.menuitems;
const updatedList = list.map((listItems)=>{
return (
<MenuItems item = { listItems } />
 );
 });
            
return(
<ul>{updatedList}</ul>);
}
const menuItems = [1, 2, 3, 4, 5];
ReactDOM.render(
<Navmenu menuitems = {
menuItems} />,
document.getElementById('root')
);
          

Uniqueness of Keys

We have told many times while discussing about keys that keys assigned to the array elements must be unique. By this, we did not mean that the keys should be globally unique. All the elements in a particular array should have unique keys. That is, two different arrays can have the same set of keys.
In the below code we have created two different arrays menuItems1 and menuItems2. You can see in the below code that the keys for the first 5 items for both arrays are the same still the code runs successfully without any warning. 

import React from "react";
import ReactDOM from "react-dom";
// Component to be extracted
function MenuItems(props) {
    const item = props.item;
    return <li>{item}</li>;
}
 
// Component that will return an
// unordered list
function Navmenu(props) {
    const list = props.menuitems;
    const updatedList = list.map((listItems) => {
        return <MenuItems key={listItems.toString()} item={listItems} />;
    });
 
    return <ul>{updatedList}</ul>;
}
 
const menuItems1 = [1, 2, 3, 4, 5];
const menuItems2 = [1, 2, 3, 4, 5, 6];
 
ReactDOM.render(
    <div>
        <Navmenu menuitems={menuItems1} />
        <Navmenu menuitems={menuItems2} />
    </div>,
    document.getElementById("root")
);