1

From what I can tell, I'm using key correctly and on the right component but the error persists despite my efforts.

I have a Notifications component that renders a list of single Notification components via array.map(). Normally this works out fine when the text for the notification is a string, but when it's JSX (like a p with a link) I keep getting the "child elements must have a unique key" warning. In this case, there is only a single notification so the key is unique.

This is the code that sets the notification in Redux:

props.setNotification({
  type: 'error',
  domain: 'page/section',
  text: (
    <p>
      An error occurred.{' '}
      <a href="/somewhere/else">Learn more</a>
    </p>
  )
})

The notifications component reads Redux and loops over the appropriate notifications:

export const Notifications = ({ notifications, type, domain }) =>
  notifications
    .filter(
      note =>
        (!type && !domain) ||
        (domain && note.domain === domain) ||
        (type && note.type === type),
    )
    .map((note, i) => (
      <Notification key={`note_${i}`} note={note} />
    )

And finally, the single notification component is just basic JSX (these components are StyledComponents):

const Notification = ({ className, note }) => (
  <Notification type={note.type} className={className}>
    <Icon name={typeIconMap[note.type]} size="18" />
    <Text>{note.text}</Text>
  </Notification>
)

The warning says:

index.js:2178 Warning: Each child in a list should have a unique "key" prop. See docs for more information.
    in p
    in div (created by Text)
    in Text (created by Notifications)
    in div (created by Notification)
    in Notification (created by Notifications)
    in Notifications (at Notifications/index.js:15)
    in Notifications (created by Connect(Notifications))

So far I've tried:

  • adding a key to the paragraph created in setNotification, but it didn't do anything despite the warning pointing to that element first.
  • using different elements instead of p, like React.Fragment, but that changed nothing, even if those elements had static keys themselves.
  • passing key into the Notification component, but that caused another error because you can't pass/access key in children.
  • changing the key that gets assigned in Notifications to something more stable than {i}. I tried {btoa(note.text)} and some other variations but nothing worked. Given that it works if text is just a string, and it even works when using a single Notification directly (manually giving it a note object instead of using Notifications, even with JSX as the text) I don't understand why this specific case would throw the error.

Is there anything obvious I'm missing here. Why does this work when text is a plain string, but not when it's JSX.

coblr
  • 3,008
  • 3
  • 23
  • 31

2 Answers2

0

I resolved this warning by assigning a random value to the key using Math.random() You can try like this:

<Notification key={Math.random()} />
sidverma
  • 1,159
  • 12
  • 24
  • This was one of the things that I tried and it doesn't change it. It's actually an anti-pattern because the key is not static enough and so React can't properly validate if the component needs to be re-rendered or not and so just does it anyway to be safe, leading to performance loss. – coblr Jun 23 '20 at 21:14
  • @coblr check this: https://stackoverflow.com/questions/34576332/warning-each-child-in-an-array-or-iterator-should-have-a-unique-key-prop-che?rq=1 – sidverma Jun 24 '20 at 03:49
0

If you add Fragements for the Notification, and give proper React Elements its will work as expected

const notificationList = [
  {
    type: "error",
    domain: "page/section",
    text: (
      <p>
        An error occurred. <a href="/somewhere/else">Learn more</a>
      </p>
    )
  }
];

const Notifications = ({ notifications, type, domain }) => (
  <>
    {notifications
      .filter(
        note =>
          (!type && !domain) ||
          (domain && note.domain === domain) ||
          (type && note.type === type)
      )
      .map((note, i) => (
        <Notification key={`note_${i}`} note={note} />
      ))}
  </>
);

const Text = props => <>{props.children}</>;
const Notification = ({ className, note }) => <Text>{note.text}</Text>;
const App = () => {
  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <Notifications
        notifications={notificationList}
        type="error"
        domain="page/section"
      />
    </div>
  );
}

Edit exciting-edison-thxlt

Raj Kumar
  • 839
  • 8
  • 13
  • Thank you for your answer and providing the snippets. Unfortunately, this still doesn't work for me. The first issue is that the `<> >` trigger invalid token warnings and won't compile correctly. Even when using React.Fragments, it's still throwing the error. I tried making Notification only return the note.text or even that wrapped in fragments but that also still throws the error, albeit with a slightly different error stack. It just makes this particular issue all the more perplexing. – coblr Jun 23 '20 at 21:10
  • Your able to replicate the issue in somewhere to check like codesandbox ? – Raj Kumar Jun 24 '20 at 01:43
  • What is <>> for if it is not a fragment???? – Arturo Hernandez Feb 14 '22 at 22:54