17

I am just starting with ReactJS and tried solutions of other questions similar to this but no luck so far.

Here is my working code :

import React from 'react';
import ReactDOM from 'react-dom';


const Numbers = ['2', '4', '6', '8'];

const NumbersList = (props) => (
   <ul>
      {
          props.Numbers.map (
             number => <li key={number}>{number * 2}</li>
          )
      }
    </ul>
)
ReactDOM.render(<NumbersList Numbers = {Numbers} />, document.getElementById('root') )

But when I am passing Numbers Array as :

const Numbers = ['4', '4', '6', '8']

I am getting this error :

Warning: Encountered two children with the same key, 4. Keys should be unique so that components maintain their identity across updates.

So my Question is : What is the best way to give keys in this situation? And if I am using Number (as in above example) as Keys, what is the best solution to avoid this warning?

Thank You!

Rajendran Nadar
  • 4,962
  • 3
  • 30
  • 51
Ankit Pandey
  • 1,790
  • 1
  • 19
  • 22

8 Answers8

19

When you don't have a definitive unique property for the items (a list of non unique primitives), you can use the index.

Note: don't use the index if the items have a unique id (and a non-primitives list should), because it's an anti-pattern.

const Numbers = ['2', '4', '4', '8'];

const NumbersList = (props) => (
  <ul>
  {
  props.Numbers.map (
    (number, index) => <li key={index}>{number * 2}</li>
  )}
  </ul>
)

ReactDOM.render(
  <NumbersList Numbers = {Numbers} />,
  document.getElementById('root')
)
<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="root"></div>
Ori Drori
  • 183,571
  • 29
  • 224
  • 209
  • 7
    But as per official Doc, we are advised not to use indexes right? So keeping good practice as a beginner, is there any other way to achieve it? – Ankit Pandey Apr 15 '18 at 10:53
  • 1
    Look at the note I've added. Usually you render a list of objects, and each should have a unique property to use as key. In this case, you don't have other options, since the values are not unique. – Ori Drori Apr 15 '18 at 10:55
  • 4
    So if we don't have unique values, the only solution I have is to give indexes as key? what if we have dynamic values and they are not unique? pardon for my beginner level questions. – Ankit Pandey Apr 15 '18 at 11:00
  • You can map the array and generate an object with unique key for each item, and whenever you add an item, add it as an object with a unique key. – Ori Drori Apr 15 '18 at 11:06
  • it dosent seem to work. Can you share jsfiddle? Thanks ! – Ankit Pandey Apr 15 '18 at 11:23
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/169019/discussion-between-ankit-pandey-and-ori-drori). – Ankit Pandey Apr 15 '18 at 11:32
  • @AnkitPandey This works most of the time, but when it doesn't, it will absolutely wreck your program. See https://jsbin.com/wohima/edit?output for an example and https://medium.com/@robinpokorny/index-as-a-key-is-an-anti-pattern-e0349aece318 for an explanation. – Marcellus Dec 07 '19 at 06:22
  • 1
    Not the best advice. When you don't have unique items you still don't have to use index alone, as when the underlying data changes VDOM will not catch the change. See https://stackoverflow.com/a/49841642/1246437 In this case one should use composite key. `const data = [1, 1, 2, 3, 4];
      {data.map((value, index) => (
    • ${value}
    • ))
    `
    – Bohdan Shulha Aug 21 '22 at 22:36
3

The reason is Each <li> tag should have a unique key. You are assigning key=4 in your <li> tag as element 4 is duplicate in your array.

After Edit

My Bad, I didn't read the full question. use uuid package to generate new id: https://www.npmjs.com/package/uuid They have documentation as well.

DadyByte
  • 894
  • 6
  • 18
  • 30
  • With due respect Sir, but you are explaining my own question :P – Ankit Pandey Apr 15 '18 at 10:55
  • @AnkitPandey I have edited my answer. Try this and let me know if this works for you. – DadyByte Apr 15 '18 at 11:02
  • it worked. I was looking for a solution without packages. Thanks for sharing this solution :) – Ankit Pandey Apr 15 '18 at 12:35
  • using `uuid` actually comes not easy. Because using `key` is meant to not repeat between renders. However `uuid` generates different value on every render. That makes inconsistent `key` usage like using `index` – Halil Kayer Aug 21 '22 at 16:04
1

Do this add a unique key as well as custom data

import React from 'react';
import ReactDOM from 'react-dom';


const Numbers = ['2', '4', '6', '8'];

const NumbersList = (props) => (
   <ul>
      {
          props.Numbers.map (
             (number, i) => <li key={i} custom={number}>{number * 2}</li>
          )
      }
    </ul>
)
ReactDOM.render(<NumbersList Numbers = {Numbers} />, document.getElementById('root') )

The key prop should be unique so I gave the returned value from the map method, custom attribute will be the array data that you want to set for all the elements.

Rajendran Nadar
  • 4,962
  • 3
  • 30
  • 51
1

Congratulations, there's no simple answer. When mapping an array of strings (most usual case) you can't be sure they are going to be unique. So you can't use values as keys.

You could've used array indices as keys. But you can't because it's bad, even if it is a really static array. You just can't, it's bad, eslint said so.

I'd advice to write a simple helper-function. Like this:

const mapArray = (source: string[]) => source.map((value, index) => ({id: index, value}))

It also mean ypu'll have to add a state to store this map and an effect to update it whenever the source changes. Or maybe you can write a hook, publish it on npm and make this world a slightly better place.

badunius
  • 61
  • 1
  • 5
0

React expects repeated items (siblings) to have a key property so that it can distinguish between items on subsequent renders. This allows it to optimise rendering of the element graph as items come, go and change.

key is meant to be a string, but React will coerce a number to string for you. Your example is quite simplistic, its more normal for data to contain some unique item value (such as database record id) so that duplicate properties can be supported but still allow items to be distinguished. In the absence of an external id property, you could decide to add a unique identifier as part of state.

It's possible to use the items index in the list, but this will not always allow React to optimise the render cycle for items. This is why its discouraged if your data contains a unique item identifier.

React checks for duplicate and missing keys to avoid the most obvious mistakes that might cause the render cycle to be less efficient.

Reacts render optimisation will really make a difference to very long lists. For an application where the scope is likely to only be a few items, there won't be much gain, and therefore array index might be a reasonable solution.

Dave Meehan
  • 3,133
  • 1
  • 17
  • 24
0

Using the index as the key is an anti-pattern and always leads to bugs in any situation.

For the best answer - what would happen if somebody change the number behind the index 1 to, for example, 5? The displayed number will remain the same, because the key was not changed.

To bypass the issues caused by using index as a key for primitives, I recommend to use composite keys.

I would write the NumbersList component the next way:

const NumbersList = ({ numbers }) => ( <ul> {numbers.map((number, index) => ( <li key={`${index}:${number}`}>{number * 2}</li> )} </ul> );

  • That is complete drop-in replacement for your variant of the `NumbersList` component. Just take a care of the naming (lowercased `numbers` prop). I've used the object destruction here which allows to skip typing the `props` variable. – Bohdan Shulha Apr 15 '18 at 16:33
  • This still has the potential for collisions. – Ryan Walker Aug 25 '22 at 02:15
  • Nope. Index + number pairs would be unique. Given array [1, 1] will produce [“0:1”, “1:1”] composite keys. There will be no collision even with the millions of same-value elements. – Bohdan Shulha Aug 25 '22 at 09:39
0

Add a variable changing the index:

{weekList.map((date, index) => {
  const x = index + 1 // <<<--- Just do it
  return(
  <React.Fragment key={x}>
    start: {date.startDate.format("YYYY-MM-DD")} end:{" "}
    {date.endDate.format("YYYY-MM-DD")}
  </React.Fragment>
)})}
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jun 16 '23 at 22:27
-3

React uses keys to identify which elements have changed, added or are removed.

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.

It is not recommended to use array indexes as key because you may update/remove the element from an array but indexes will remain same so React will not be able to identify the change and will display the wrong results.

In your code, you don't have any unique id for array elements so the only thing you can use to make it unique is array indexes.

const Numbers = ['2', '4', '4', '8'];

const NumbersList = (props) => (
  <ul>
  {
  props.Numbers.map (
    (number, index) => <li key={index}>{number * 2}</li>
  )}
  </ul>
)

ReactDOM.render(
  <NumbersList Numbers = {Numbers} />,
  document.getElementById('root')
)
<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="root"></div>
Ankit Chaurasia
  • 785
  • 7
  • 7