error: Do not use Array index in keys

ReactjsNpm

Reactjs Problem Overview


I am using index to generate key in a list. However, es-lint generates an error for the same. React doc also states that using the item index as a key should be used as last resort.

const list = children.map((child, index) =>
    <li key={index}> {child} </li>);

I considered using react-key-index. npm install react-key-index gives following error:

npm ERR! code E404

npm ERR! 404 Not Found: react-key-index@latest

Are there any suggestions on other packages that allow to generate unique key? Any suggestion on react key generator is appreciated!

Reactjs Solutions


Solution 1 - Reactjs

When you use index of an array as a key, React will optimize and not render as expected. What happens in such a scenario can be explained with an example.

Suppose the parent component gets an array of 10 items and renders 10 components based on the array. Suppose the 5th item is then removed from the array. On the next render the parent will receive an array of 9 items and so React will render 9 components. This will show up as the 10th component getting removed, instead of the 5th, because React has no way of differentiating between the items based on index.

Therefore always use a unique identifier as a key for components that are rendered from an array of items.

You can generate your own unique key by using any of the field of the child object that is unique as a key. Normal, any id field of the child object can be used if available.

Edit : You will only be able to see the behavior mentioned above happen if the components create and manage their own state, e.g. in uncontrolled textboxes, timers etc. E.g. https://stackoverflow.com/questions/46477711/react-error-when-removing-input-component

Solution 2 - Reactjs

The issue with using key={index} happens whenever the list is modified. React doesn't understand which item was added/removed/reordered since index is given on each render based on the order of the items in the array. Although, usually it's rendered fine, there are still situations when it fails.

Here is my example that I came across while building a list with input tags. One list is rendered based on index, another one based on id. The issue with the first list occurs every time you type anything in the input and then remove the item. On re-render React still shows as if that item is still there. This is  UI issue that is hard to spot and debug.

enter image description here

    class List extends React.Component {
      constructor() {
        super();
        this.state = {
          listForIndex: [{id: 1},{id: 2}],
          listForId: [{id: 1},{id: 2}]
        }
      }
      renderListByIndex = list => {
        return list.map((item, index) => {
          const { id } = item;
          return (
              <div key={index}>
                <input defaultValue={`Item ${id}`} />
                <button 
                  style={{margin: '5px'}} 
                  onClick={() => this.setState({ listForIndex: list.filter(i => i.id !== id) })}
                 >Remove</button>
              </div>
          )
        })
      }
      renderListById = list => {
        return list.map((item) => {
          const { id } = item;
          return (
              <div key={id}>
                <input defaultValue={`Item ${id}`} />
                <button 
                  style={{margin: '5px'}} 
                  onClick={() => this.setState({ listForId: list.filter(i => i.id !== id) })}
                 >Remove</button>
              </div>
          )
        })
      }
      render() {
        const { listForIndex, listForId } = this.state;
        return (
          <div className='flex-col'>
            <div>
              <strong>key is index</strong>
              {this.renderListByIndex(listForIndex)}
            </div>
            <div>
              <strong>key is id</strong>
              {this.renderListById(listForId)}
            </div>
          </div>
        )
      }
    }

    ReactDOM.render(
      <List />,
      document.getElementById('root')
    );

.flex-col {
  display: flex;
  flex-direction: row;
}

.flex-col > div {
  flex-basis: 50%;
  margin: .5em;
  padding: .5em;
  border: 1px solid #ccc;
 }

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
    <div id="root">
        <!-- This element's contents will be replaced with your component. -->
    </div>

Solution 3 - Reactjs

Of course, in React, you are required to pass in a unique key value for all elements of an array. Else, you will see this warning in console.

> Warning: Each child in an array or iterator should have a unique “key” prop.

So, as a lazy developer, you would simply pass in the loop’s index value as the key value of the child element.

Reordering a list, or adding and removing items from a list can cause issues with the component state, when indexes are used as keys. If the key is an index, reordering an item changes it. Hence, the component state can get mixed up and may use the old key for a different component instance.

What are some exceptions where it is safe to use index as key?

  • If your list is static and will not change.
  • The list will never be re-ordered.
  • The list will not be filtered (adding/removing item from the list).
  • There are no ids for the items in the list.

Key should be unique but only among its siblings.

Solution 4 - Reactjs

Do not use array indexes as keys, this is an anti-pattern that is pointed out by the React team in their docs.

It's a problem for performance and for state management. The first case applies when you would append something to the top of a list. Consider an example:

<ul>
  <li>Element1</li>
  <li>Element2</li>
  <li>Element3</li>
</ul>

Now, let say you want to add new elements to the top/bottom of the list, then reorder or sort the list (or even worst - add something in the middle). All the index-based key strategy will collapse. The index will be different over a time, which is not the case if for each of these elements there would be a unique id.

CodePens:

Play with it and you'll see that at some point the index-based key strategy is getting lost.

Solution 5 - Reactjs

use the following lib "react-uuid" : https://www.npmjs.com/package/react-uuid.

react-uuid basically create random ids when you call it each time.

import React from 'react'
import uuid from 'react-uuid'

const array = ['one', 'two', 'three']

export const LineItem = item => <li key={uuid()}>{item}</li>

export const List = () => array.map(item => <LineItem item={item} />)

and this should solve the issue.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionTanvi PatelView Question on Stackoverflow
Solution 1 - ReactjspalsrealmView Answer on Stackoverflow
Solution 2 - ReactjsRachel NicolasView Answer on Stackoverflow
Solution 3 - ReactjsDarshan SachaniyaView Answer on Stackoverflow
Solution 4 - ReactjsDaniel DanieleckiView Answer on Stackoverflow
Solution 5 - ReactjsHADDADView Answer on Stackoverflow