Home > Software engineering >  Each child in a list should have a unique "key" prop error despite method to create unique
Each child in a list should have a unique "key" prop error despite method to create unique

Time:08-16

In my application I am currently getting the react warning:

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

Check the render method of GetReplies.

This is the GetReplies method:

export function GetReplies(props) {
    const Id = props.Id;
    const replies = allComments.filter(obj => obj.ParentCommentId === Id).
        sort((objA, objB) => new Date(objB.Time) - new Date(objA.Time));
    console.log(generateKey(Id));
        
        if (Object.keys(replies).length !== 0) {
    return (
            <div key = {"replies-container-"   generateKey(Id)} id={"replies-container-"   Id} className="replies-container">
            <div key ={"panel-heading replies-title"   generateKey(Id)} className="panel-heading replies-title">
                <a key = {"accordion-toggle replies-"   generateKey(Id)} className="accordion-toggle replies-a collapsed" data-parent={"#replies-container-"   Id} data-toggle="collapse" data-target={"#replies-for-"   Id}>Replies</a>
            </div>
            <div key = {"replies-for-"   Id} id={"replies-for-"   generateKey(Id)} className="replies-list collapse">
                    {    
                        <React.Fragment>
                      {  Object.entries(replies).reverse().map(([key, arr]) => {
                          return (
                                <GetComments commentsArray = {replies}/>
                           )

                          }) }
                        </React.Fragment>
                    }
            </div>
        </div>

    );

    }       
}

and this is the GetComments Method it calls:

export function GetComments({ commentsArray }) {
  return (
    <React.Fragment>
      {commentsArray.map((comment) => {
         const localId = comment.LocalId;
        const parentCommentId = comment.ParentCommentId;
        const parentLocalId = allComments.filter(obj => obj.Id === parentCommentId);
        const recipients = comment.Recipients;
        let recipientsArray = [];
        let recipientsList;
         recipients.forEach(function (arrayItem) {
            recipientsArray.push(arrayItem.Name);
            recipientsList = recipientsArray.join(', ');
        });
        console.log(generateKey(localId));
        const date = new Date(comment.Time);
        const formattedDate = date.toLocaleDateString()   " "   ("0"   date.getHours()).slice(-2)   ":"   ("0"   date.getMinutes()).slice(-2);
        return (
          <div key={generateKey(localId)} className="comment-container">
            <div key={generateKey(comment.Commenter.ItemId)} className="commenter">
              <span className="id-label">{localId}</span>
              {parentCommentId && (
                <span className="reply" title={`in reply to ${parentLocalId[0].LocalId}`}>
                    <a className="reply" href={"#c"   parentLocalId[0].LocalId}>&#x2935;</a> </span>
              )}
            <span><a id={"c"   localId} name={"c"   localId}>{comment.Commenter.Name}</a></span>
            <div key={generateKey(localId)   "-comment-actions-container "} className="comment-actions-container">
                <button type="button" className="btn-reply" data-value={comment.Id} title="Reply to comment" data-toggle="modal" data-target="#dlg-new-comment">&#x2945;</button>
            </div>
            </div>
            <div key={generateKey(localId)   "-recipients "} className="recipients">{recipientsList}</div>
            <div key={generateKey(localId)   "-comment "} className="comment">{comment.Comment}</div>
            <div key={generateKey(localId)   "-comment-footer "} className="comment-footer">{formattedDate}</div>
           <GetReplies Id = {comment.Id}/>
          </div>
        );
      })}
    </React.Fragment>
  );
}

To help make sure each key is unique I made this generate key method:

const generateKey = (pre) => {
    return `${ pre }_${ new Date().getTime() }`;
}

However I am still getting that unique key error and I have no idea what it is I could be missing?

I am even tried to expand upon it by doing:

const generateKey = (pre) => {
    let index =0;
    return `${ pre }_${ new Date().getTime()}_${index  }`;
}

but index would always equal 0? So I'm not sure why that wasn't incrementing either, any advice would be appreciated

CodePudding user response:

Even though your generateKey might work (not sure though since it will get called quite quickly when mapping over an array) you are adding the key to the wrong part of your code. React expects a key on every item in a for-loop. In your case you have added a key to every JSX element, except for those within the Object.entries(replies).reverse().map

You could probably fix your problem by adding keys in there, like so:

    {Object.entries(replies).reverse().map(([key, arr]) => {
      return (
          <GetComments key={key} commentsArray = {replies}/>
        )
     })}

To add a note though, React uses the key value to recognize changes on re-renders. In case your array gets re-ordered it can use the keys to prevent enormous re-renders. The best practice here would be to add a key that is related to the data itself, not the location in an array or a random number.

CodePudding user response:

In addition of the previous answer :

const generateKey = (pre) => {
    let index =0;
    return `${ pre }_${ new Date().getTime()}_${index  }`;
}

will always have a 0 index, because each function call will create his own scope, with his own "index" variable. To increment the index on each call, you must extract the "index" variable outside of the function, so each function call will share the same index variable, from the 'outside' scope (but using the item's index remains the best idea, if you have no unique key you can use from your data).

Using "new Date().getTime()" to generate keys is also a bad idea, because your code will be ran so quick that a few components could (and will) share the same timestamp.

An alternative is to use a third party library, like 'uuid', to generate unique ids, but it must be used carefully.

CodePudding user response:

You should use the index of the commentsArray.map

like :

{commentsArray.map((comment, index) => {
...
key={generateKey(Id, index)}

and then :

const generateKey = (pre, index) => `${pre}_${index}`;

You are filtering with Id values, so your key will be the same for each childs, that's why you need the commentsArray index.

  • Related