12

I often run into the error

Warning: Each child in a list should have a unique "key" prop.  Check the render method of `MyComponent`.

in React. The error message always tells you the offending component, but not the specific HTML tag / virtual DOM element that is offending. Working in a large code base with sometimes big components, this makes finding the source of the error very difficult.

What causes this error? I'm looking for a definitive list.

  • A tag in an array with a "key" prop missing entirely (pretty sure)
  • Two tags in an array with the same "key" prop value? (i thought there was a different error message for this)

Do two elements written side-by-side (such as <div></div><div></div>) count as "children in a list"? Will they also cause the error?

What are effective strategies for finding the offensive tag?

  • adding key={Math.random()} to every single keyless tag in the component, one-by-one, until the error disappears, and then seeing which one you added last. (can be time consuming, and sometimes doesn't work)
  • undoing changes chronologically until the error disappears. (can be time consuming)
  • something better here

I am looking for a thorough and canonical answer.

mareoraft
  • 3,474
  • 4
  • 26
  • 62
  • 4
    Most probably, you use `map()` inside one of your components to transform an array into JSX elements. While doing so, I suspect, you didn't pass `key` property to those elements. You should have done something, like: `arr.map((element,key) =>
    {element}
    )`
    – Yevhen Horbunkov Jan 24 '20 at 20:52
  • With above approach, elements produced by common `map()` will have unique `key` values (as second argument of `map()` refers to the index of item within array). `Math.random()`, theoretically, has certain chances to produce same output twice or more times, so, I don't think that would be a good practice to use it. – Yevhen Horbunkov Jan 24 '20 at 20:55
  • @SherylHohman No, it does not. Please read the question carefully. – mareoraft Feb 03 '20 at 15:05
  • 1
    If you want to use random keys, best choise is to use [nanoid](https://www.npmjs.com/package/nanoid#react). – Christos Lytras Feb 03 '20 at 19:18
  • Every time you map array to list in JSX you are able to use this arrays indexes as keys. As mentioned above as well as below, this error only appers if the list is generated dynamically out of array of your data. As key is supposed to be unique, random method from Math library wont do the trick. – Martin Plávek Feb 07 '20 at 09:57
  • Does this answer your question? [Understanding unique keys for array children in React.js](https://stackoverflow.com/questions/28329382/understanding-unique-keys-for-array-children-in-react-js) – Liam May 11 '22 at 15:49

7 Answers7

18

You can find offending part by looking for map calls in your jsx. Each top-level element inside map should have key property, i.e.

{items.map(item => (
  <div key={item.id}>
    <div>{item.name}</div>
    <div>{item.description}</div>
  </div>
))}

Docs have some explanations on the subject, in particular:

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

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

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

We don’t recommend using indexes for keys if the order of items may change. This can negatively impact performance and may cause issues with component state.

UPD

If you want to use Math.random, I think the better solution might be using UUIDv4. For example, this package can generate them. While theoretically it is possible to generate two similar UUIDs, chance is very low and you need to generate a lot in seconds (some numbers). However, I never did that and can't say how much using UUID as key impacts performance. Given what documentation says about keys, I guess react will always think that all elements were removed and new ones added.

So the best solution is to have some id associated with each item. If you render an array of unique strings, item itself can be the key. If items in array do not have any unique id and order of items is never changed and items can not be removed from array, using index should be a safe option. And as last resort you can try uuid.

UPD2

As regards to finding offensive code, I noticed that there is a trace in this warning, looking like this:

index.js:1375 Warning: Each child in a list should have a unique "key" prop.

Check the render method of `Log`. See https://*b.me/react-warning-keys for more information.
    in div (at Log.js:241)
    in Log (created by ConnectFunction)
    in ConnectFunction (at HomePage.js:10)
    in WithWorkspace (created by ConnectFunction)
    in ConnectFunction (at HomePage.js:8)
    in HomePage (at App.js:24)
    in Route (at AuthenticatedRoute.js:14)
    in AuthenticatedRoute (created by ConnectFunction)
    in ConnectFunction (at App.js:23)
    in Switch (at App.js:22)
    in div (at App.js:21)
    in div (at App.js:18)
    in Unknown (created by ConnectFunction)
    in ConnectFunction (at FetchAll.js:165)
    in Unknown (created by ConnectFunction)
    in ConnectFunction (at FetchAll.js:164)
    in Unknown (created by ConnectFunction)
    in ConnectFunction (at FetchAll.js:163)
    in FetchAll (at App.js:17)
    in Router (created by BrowserRouter)
    in BrowserRouter (at App.js:15)
    in App (at src/index.js:14)
    in Provider (at src/index.js:13)

Here offending file is named Log.js, line 241. I don't know if trace is always present and correct but it might help.

As for me, I check result in browser very often and console usually is open, so when I see that warning, I usually know what I did with arrays just recently and where I forgot the key.

Gennady Dogaev
  • 5,902
  • 1
  • 15
  • 23
10

Here is a partial answer based on what I've learned so far.

What does/doesn't cause this error? Here is a list:

  1. A tag in an array with a "key" prop missing causes the error. For example,

    <Fragment>
        {[
            <div>one</div>
        ]}
    </Fragment>
    

gives the error, regardless of the number of children.

  1. A tag not in an array with a "key" prop missing does not cause the error. For example,

    <Fragment>
        <div>one</div>
    </Fragment>```
    

does not give the error, regardless of the number of children.

  1. A tag in an array with a "key" prop present with a value of undefined causes the error. For example,

    <Fragment>
        {[
            <div key={undefined}>one</div>
        ]}
    </Fragment>```
    

gives the error, even though the key prop is typed out. It's important to be aware of this, because it means that you could assign a variable to the key prop and still run into this error. For example, you might have bad data coming into your application so that key={myobj.id} triggers the error because myobj.id is undefined.

  1. A tag in an array with a "key" prop present with duplicate defined values does not cause the error. For example,

    <Fragment>
        {[
            <div key={'chicken'}>one</div>,
            <div key={'chicken'}>one</div>
        ]}
    </Fragment>```
    

does not give the error, even though the keys are not unique!

What causes this error? In summary:

The error is caused exactly when there exists an Array containing an item that is a tag which has no key prop assigned or has a key prop with an assigned value of undefined.

mareoraft
  • 3,474
  • 4
  • 26
  • 62
3

When you have to render an array in React, you would be using the map function.

If you have a map function in your render component, the root element it returns takes a key attribute, which must be unique. This is to optimise the rendering of the list.

const names = ['John', 'Sam', 'Charlie'];
{
  names.map( (name, index) =>
    <div key={index}>
      <Foo />
      <Bar />
    </div>
  )
}

To fix the issue in your MyComponent, you should first identify where you are mapping the elements, and then, add the key attribute. If there is nothing as a unique identifier in your array, even the index (as mentioned in the code snippet above) is a good candidate. If it were an array of objects of users; email ID or user ID looks promising.

acagastya
  • 323
  • 2
  • 14
3

Strategies

There is an ESLint rule that could be used to prevent this mistake to happen again.

JSX-Key missing warning:

How the rule works?

Warn if an element that likely requires a key prop--namely, one present in an array literal or an arrow function expression.

Invalid

[<Hello />, <Hello />, <Hello />];

Valid

[<Hello key="first" />, <Hello key="second" />, <Hello key="third" />];

Link:

https://github.com/yannickcr/eslint-plugin-react/blob/HEAD/docs/rules/jsx-key.md

2

In case you have an empty tag <> something </> which wraps something, you have to rewrite it as <React.Fragment key={some_key}> something </React.Fragment>

https://www.designcise.com/web/tutorial/how-to-add-a-key-to-an-empty-tag-in-react

1

@mareoraft and @Gennady Dogaev gave great answers and answered most of your questions.

These are my 2 cents:

What causes this error? I'm looking for a definitive list.

A tag in an array with a "key" prop missing entirely (pretty sure)

Yep! You either have a duplicate key or key is missing entirely

Two tags in an array with the same "key" prop value? (i thought there was a different error message for this)

Two elements with same keys will throw the same warning. The warning is not showed in production mode, only in dev mode. Having duplicate keys can also result in weird behaviour: the elements with identical keys won't update properly or keep the old props. This is not noticeable if the elements of you lists don't change - ex: the elements in the rendered list never change.

Do two elements written side-by-side (such as ) count as "children in a list"? Will they also cause the error?

No. This will not cause an error. "Keys help React identify which items have changed, are added, or are removed." - more details Those divs are static code - they never change so they don't need a key.


What are effective strategies for finding the offensive tag?

adding key={Math.random()} to every single keyless tag in the component, one-by-one, until the error disappears, and then seeing which one you added last. (can be time consuming, and sometimes doesn't work)

Using random keys for your list items isn't a great idea. Having a random key generated on every render means that all you list components will update (re-render) even if the props haven't changed. I would use this as a last resort and for large lists (or apps) this can have performance issues.

When I don't have an id to use as key, rather than random, I like to use the index and compose a key - ex: list.map((elem, index) => <div key={`some-element-${index}`}>{elem.name}</div>) Using index as key is considered an anti-pattern but it can sure help you get passed this issue.

Having composed names for keys you can easily find the component that raised the warning - ex: find add some-element- in your code since the warning shows the name of the duplicate key.

undoing changes chronologically until the error disappears. (can be time consuming)

This might work but you are right: it's time consuming - then again, what isn't? :)

something better here

You could give eslint-plugin-react a try. They have a jsx-key rule that might help you. Although it might still have some limitations it's still more than nothing.

Hope this helps!

bamse
  • 4,243
  • 18
  • 25
0

What really helped me to understand why arrays need keys in React was remembering that React is declarative programming.

You don't have to juggle addEventListener, removeEventListener or manage state implicitly anymore. You just give React a state object and a layout in JSX and it figures it out as a user reacts with your application.

For the magic to work, React turns your UI into a big pile of objects and runs a diff during reconciliation that compares the current UI with how it should change. Anything that doesn't match the intended UI gets subbed out.

Arrays are a particular challenge because they represent lists, which frequently get sorted, filtered, etc in the UI. When React performs reconciliation without keys, all list items get re-rendered. But keys give React a cheap way to do those comparisons between objects; matches don't require another render.

serraosays
  • 7,163
  • 3
  • 35
  • 60