ChatGPT解决这个技术问题 Extra ChatGPT

Understanding unique keys for array children in React.js

I'm building a React component that accepts a JSON data source and creates a sortable table. Each of the dynamic data rows has a unique key assigned to it but I'm still getting an error of:

Each child in an array should have a unique "key" prop. Check the render method of TableComponent.

My TableComponent render method returns:

<table>
  <thead key="thead">
    <TableHeader columns={columnNames}/>
  </thead>
  <tbody key="tbody">
    { rows }
  </tbody>
</table>

The TableHeader component is a single row and also has a unique key assigned to it.

Each row in rows is built from a component with a unique key:

<TableRowItem key={item.id} data={item} columns={columnNames}/>

And the TableRowItem looks like this:

var TableRowItem = React.createClass({
  render: function() {

    var td = function() {
        return this.props.columns.map(function(c) {
          return <td key={this.props.data[c]}>{this.props.data[c]}</td>;
        }, this);
      }.bind(this);

    return (
      <tr>{ td(this.props.item) }</tr>
    )
  }
});

What is causing the unique key prop error?

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.
Can you also share rows array or more preferably a jsfiddle? You dont need a key property on thead and tbody by the way.
I added the row component to the original question @nilgun.
Is it possible that some items do not have an id or have same id?

L
Liam

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


I am running into the exact same error. Was this resolved after the chat? If so, can you please post an update to this question.
Why is is so hard for React to generate unique keys itself?
This is pretty much the official word on a note made in the issue chat linked to above: keys are about identity of a member of a set and auto-generation of keys for items emerging out of an arbitrary iterator probably has performance implications within the React library.
Is there documentation explaining why not only the child nodes need a unique key, but also the children of those child nodes? I couldn't find anything about that specifically in the docs.
C
Community

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(

    {this.state.arr.map( (item, i) => {item + " "} )}
); } } const Item = (props) => { return (
  • ); } ReactDOM.render(, document.getElementById("app"));

    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.


    In the official docs, they use toString() to convert to string instead of leaving as number. Is this important to remember?
    @skube, no, you can use integers as key as well. Not sure why they are converting it.
    @skube, yes that is perfectly acceptable. As stated in the examples above, you can use the item's index of the iterated array (and that's an integer). Even the docs state: "As a last resort, you can pass item's index in the array as a key". What happens though is that the key always ends up being a String anyway.
    should this key be unique only in that array or should be unique in the whole App?
    @farmcommand2, keys are applied to React Components, and need to be unique among siblings. This is stated above. In other words, unique in the array
    P
    Penny Liu

    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.


    This was extremely helpful, thank you! I didn't even realize I had to put it in the outermost layer
    I was doing similar groupings, but with table rows. You cannot wrap a table row in a div. Instead, wrap it in a react Fragment with a key, as demonstrated here: stackoverflow.com/a/51875412/750914
    Thank you. This helped me. I was using <> inside map() Removing that helped me.
    <> is a short syntax for . So if you want to add a key you can do like this:
    I had the same issue using Material-UI where I was using a List (and elsewhere a Menu) and placing the child items of those components inside a Tooltip. Giving the Tooltip the "key" instead of the ListItem/MenuItem itself got rid of the warning.
    G
    Gerd

    Check: key = undef !!!

    You got also the warn message:

    Each child in a list should have a unique "key" prop.
    

    if your code is complete right, but if on

    <ObjectRow key={someValue} />
    

    someValue is undefined!!! Please check this first. You can save hours.


    literally saved!
    M
    Mahdi Bashirpour

    Just add the unique key to the your Components

    data.map((marker)=>{
        return(
            <YourComponents 
                key={data.id}     // <----- unique key
            />
        );
    })
    

    F
    Foyez

    You should use a unique value for each children key of tbody where

    the value cannot not be identical (same) to its sibling

    should not change between renders

    For example, the key value can be database id or UUID (Universal Unique Identifier).

    Here the keys are handling manually:

    <tbody>
      {rows.map((row) => <ObjectRow key={row.uuid} />)}
    </tbody>
    

    You can also let React handle the keys using React.Children.toArray

    <tbody>
      {React.Children.toArray(rows.map((row) => <ObjectRow />))}
    </tbody>
    

    S
    Stephen Rauch

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

    This is a warning as for array items which we are going to iterate over will need a unique resemblance.

    React handles iterating component rendering as arrays.

    Better way to resolve this is provide index on the array items you are going to iterate over.for example:

    class UsersState extends Component
        {
            state = {
                users: [
                    {name:"shashank", age:20},
                    {name:"vardan", age:30},
                    {name:"somya", age:40}
                ]
            }
        render()
            {
                return(
                        <div>
                            {
                                this.state.users.map((user, index)=>{
                                    return <UserState key={index} age={user.age}>{user.name}</UserState>
                                })
                            }
                        </div>
                    )
            }
    

    index is React built-in props.


    This approach is potentially dangerous if the items are rearranged somehow. But if they remain static then this is fine.
    @chris I completely agree with you because in this case index may be duplicated. Better to use dynamic values for key.
    @chris I also agree with your comment. We should use dynamic values rather then index because there may be duplicates. To make it simple I did this. Btw thanks for your contribution (upvoted)
    N
    Nasreen Ustad

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

    const todoItems = todos.map((todo, index) =>
    // Only do this if items have no stable IDs
       <li key={index}>
          {todo.text}
       </li>
    );
    

    Please refer to List and Keys - React


    D
    Dharman

    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.

    Example:

    function List() {
      const numbers = [0,1,2,3];
     
      return (
        <ul>{numbers.map((n) => <li> {n} </li>)}</ul>
      );
    }
    
     ReactDOM.render(
      <List />,
      document.getElementById('root')
    );
    

    In the above example, it creates a dynamic list using li tag, so since li tag does not have a unique key it shows an error.

    After fixed:

    function List() {
      const numbers = [0,1,2,3];
     
      return (
        <ul>{numbers.map((n) => <li key={n}> {n} </li>)}</ul>
      );
    }
    
     ReactDOM.render(
      <List />,
      document.getElementById('root')
    );
    

    Alternative solution when use map when you don't have a unique key (this is not recommended by react eslint ):

    function List() {
      const numbers = [0,1,2,3,4,4];
     
      return (
        <ul>{numbers.map((n,i) => <li key={i}> {n} </li>)}</ul>
      );
    }
    
     ReactDOM.render(
      <List />,
      document.getElementById('root')
    );
    

    Live example: https://codepen.io/spmsupun/pen/wvWdGwG


    wow, tnx for simple solution! key={i} does the trick for me
    K
    Khadim H.

    Best solution of define unique key in react: inside the map you initialized the name post then key define by key={post.id} or in my code you see i define the name item then i define key by key={item.id}:

    {posts.map(item =>(
    {item.name}

    {item.username}

    {item.email}

    ))}


    F
    Felipe

    I was running into this error message because of <></> being returned for some items in the array when instead null needs to be returned.


    A
    Arvind

    I had a unique key, just had to pass it as a prop like this:

    <CompName key={msg._id} message={msg} />
    

    This page was helpful:

    https://reactjs.org/docs/lists-and-keys.html#keys


    s
    superup

    In my case, set id to tag

    <tbody key={i}>
    

    The problem is solved.


    p
    prime

    This is a warning, But addressing this will make Reacts rendering much FASTER,

    This is because React needs to uniquely identify each items in the list. Lets say if the state of an element of that list changes in Reacts Virtual DOM then React needs to figure out which element got changed and where in the DOM it needs to change so that browser DOM will be in sync with the Reacts Virtual DOM.

    As a solution just introduce a key attribute to each li tag. This key should be a unique value to each element.


    This is not entirely correct. Rendering will not be faster if you add the key prop. If you don't provide one, React will assign one automatically (the current index of the iteration).
    @Chris in that case why it raise a warning ?
    because by not providing a key, React doesn't know how your data is modelled. This can lead to undesired results if the array is modified.
    @Chris in that case of array modification, will React correct the indices according to that if we did not provide keys. Anyway I thought removing extra overhead from React will make some impact on the rendering process.
    again, React will basically do key={i}. So it depends on the data your array contains. For example, if you have the list ["Volvo", "Tesla"], then obviously the Volvo is identified by the key 0 and the Tesla with 1 - because that is the order they will appear in the loop. Now if you reorder the array the keys are swapped. For React, since "object" 0 is still at the top, it will rather interpret this change as a "rename", rather than a reorder. The correct keys here would need to be, in order, 1 then 0. You don't always reorder mid runtime, but when you do, it's a risk.
    R
    Ramesh
    var TableRowItem = React.createClass({
      render: function() {
    
        var td = function() {
            return this.props.columns.map(function(c, i) {
              return <td key={i}>{this.props.data[c]}</td>;
            }, this);
          }.bind(this);
    
        return (
          <tr>{ td(this.props.item) }</tr>
        )
      }
    });
    

    This will sove the problem.


    V
    Vik2696
    If you are getting error like :
    
    > index.js:1 Warning: Each child in a list should have a unique "key" prop.
    
    Check the render method of `Home`. See https://reactjs.org/link/warning-keys for more information.
    
    Then Use inside map function like:
    
      {classes.map((user, index) => (
                  <Card  **key={user.id}**></Card>
      ))}`enter code here`
    

    L
    L3xpert

    This is a simple example,I have used a react condition with && first then map, in the I have added the key the user id to be sure that it's unique

     <tbody>
                                        {users &&
                                        users.map((user) => {
                                            return <tr key={user._id}>
                                                <td>{user.username}</td>
                                                <td><input
                                                    name="isGoing"
                                                    type="checkbox"
                                                    checked={user.isEnabled}
                                                    onChange={handleInputChangeNew}/></td>
                                                <td>{user.role.roleTitle} - {user.role.department.departmentName}</td>
                                                {/*<td className="text-right">
                                                        <Button>
                                                            ACTION
                                                        </Button>
                                                    </td>*/}
                                            </tr>
                                        })
                                        }
    
    
                                        </tbody>
    

    N
    Niloy dey
    if we have array object data . then we are map for showing  the data . and pass the unique id (key = {product.id} ) because browser can selected the unique data 
    
    example : [
        {
            "id": "1",
            "name": "walton glass door",
            "suplier": "walton group",
            "price": "50000",
            "quantity": "25",
            "description":"Walton Refrigerator is the Best Refrigerator brand in bv 
             Bangladesh "
        },
        {
            
            "id": "2",
            "name": "walton glass door",
            "suplier": "walton group",
            "price": "40000",
            "quantity": "5",
            "description":"Walton Refrigerator is the Best Refrigerator brand in 
             Bangladesh "
        },
    }
    
    
    now we are maping the data and pass the unique id: 
    {
        products.map(product => <product product={product} key={product.id} 
        </product>)
    }
    

    B
    Basurp Patil

    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

    https://i.stack.imgur.com/B0UWZ.png

    https://i.stack.imgur.com/WbMzO.png


    While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - From Review
    D
    Dila Gurung

    I don't go with the detail explanation but key to this answer is "key" just put the key attribute in your tag and ensure that every-time you iterate you give unique value to it

    #ensure that key's value is not clashing with others

    Example

    <div>
            {conversation.map(item => (
              <div key={item.id  } id={item.id}>
              </div>
            ))}
          </div>
    

    where conversation is an array something like below :

      const conversation = [{id:"unique"+0,label:"OPEN"},{id:"unique"+1,label:"RESOLVED"},{id:"unique"+2,label:"ARCHIVED"},
       ]
    

    A
    Atish Barua

    your key should be unique.like an unique id.And your code should be like this

    <div>
     {products.map(product => (
       <Product key={product.id}>
        </Product>
        ))}
      </div>
    

    A
    A K M Intisar Islam

    I faced a similar problem but not exact. Tried every possible solution and couldn't get rid of that error

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

    Then I tried opening it in a different local host. I don't know how, but it worked!


    ` rowKey={(item) => item.id}` attribute inside the table tag solved my issue
    This is most likely an issue with browser cache. You can hard-refresh on the same local host to remove any stored cache.
    S
    Saurab

    If you are struggling with this error Each child in a list should have a unique "key" prop.

    Solve by declaring index value to the key attribute inside the rendering element.

    App.js component

    import Map1 from './Map1';
    
    const arr = [1,2,3,4,5];
    
    const App = () => {
      return (
        <>
         
         <Map1 numb={arr} />     
    
        </>
      )
    }
    
    export default App
    

    Map.js component

    const Map1 = (props) => {
    
        let itemTwo = props.numb;
        let itemlist = itemTwo.map((item,index) => <li key={index}>{item}</li>)
    
        return (
            <>        
            <ul>
                <li style={liStyle}>{itemlist}</li>
            </ul>
            </>
        )
    }
    
    export default Map1