Skip to content

Each child in an array does not need a unique key #8920

Closed

Description

@neaucode

Note the keys 'react-is' and 'very-stupid'

const welcomeUser = this.state.existingCookie.username && [
  <div className={CSS.welcomeUser} key='react-is'>
    {i18n.t('Welcome ') + this.state.existingCookie.username}</div>,
  <div className={CSS.pleaseLogIn} key='very-stupid'>
    {i18n.t('Please log in')}</div>
];

return (
  <div className={CSS.sky}>
    {welcomeUser}</div>
);

Do you want to request a feature or report a bug?
No

What is the current behavior?
Cannot pass around array of JSX without unnecessary keys

If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem via https://jsfiddle.net or similar (template: https://jsfiddle.net/reactjs/69z2wepo/).

What is the expected behavior?
React shouldn't care if I put keys on my own elements, and should not console.warn

Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?
Latest

Activity

  1. aweary commented on Feb 2, 2017

    @aweary
    Contributor

    Hey @neaumusic, thanks for the issue. React cares about keys when using arrays because arrays typically represent mapped data. In many cases, this data can and will be reorganized (e.g., a todo list that can add/remove/reorder items). Keys help React do that efficiently. They aren't required, which is why it's only a warning and not an error, but they are highly recommended in that case.

    The issue here is that you're using an array as a fragment, presumably to avoid having to return a single wrapping node around the two. While this works, React cannot differentiate between an array of mapped data and an array representing a "fragment", and since there is no explicit support for fragments at the moment it assumes the array represents mapped data.

    React Fiber provides a way to return fragments, so future major releases should make this easier.

    That said, I'm going to close this out as I don't think we want to increase the complexity of handling arrays to support this right now.

  2. neaucode commented on Feb 2, 2017

    @neaucode
    Author

    You're right that I don't want to use a wrapping node for no reason other than to appease React

    Thanks for the reply. Yea arrays are sequential maps of data, even if it looks like JSX. There is no difference between an array of mapped data and a fragment, which is why I don't understand how you're saying there is a difference. If the user wants to put the same template twice, he should definitely be able to do so, and React can create it's own index if it really can't keep instances straight.

    I think what you're saying is that React can't tell if the user reverses the order of the templates, but if the user uses the exact same template, they should both be updating exactly the same

    My number one complaint about react is that your render method becomes a slalom in order to use refs, and this is just another example of bad template management on React's part, imo

  3. aweary commented on Feb 2, 2017

    @aweary
    Contributor

    There is no difference between an array of mapped data and a fragment, which is why I don't understand how you're saying there is a difference.

    The difference is that mapped data is typically dynamic, and an array representing a fragment is not. Dynamic arrays are by far the most common use case and greatly benefit from unique keys, which is why the warning is in place.

    // Dynamic data, likely to be reordered or resized
    <div>
       {this.state.foo.map( foo => <Foo {...foo} />)}
    </div>
    
    // Static fragment, not likely to be reordered
    <div>{[<Foo />, <Bar />]}</div>

    There's no difference from React's perspective, which is why you get the warning even though you're not mapping anything or doing any dynamic updates to the array. We understand this is a pain point, and as mentioned future versions of React should address this.

    See #2127 for some background.

  4. gaearon commented on Feb 2, 2017

    @gaearon
    Contributor

    In a future version of JSX, I think we’re aiming to support a syntax like this:

    const welcomeUser = this.state.existingCookie.username &&
      <>
        <div className={CSS.welcomeUser} key='react-is'>
          {i18n.t('Welcome ') + this.state.existingCookie.username}
        </div>
        <div className={CSS.pleaseLogIn} key='very-stupid'>
          {i18n.t('Please log in')}
        </div>
      </>;

    It will make sense especially after the next version of React is out, since it will allow returning arrays from a component. I think having such separate syntax would solve the problem because, while it still produces an array, it could let React distinguish between the case where keys are necessary (dynamic lists), and the static lists.

    Unfortunately, until then, this is as good as it gets. Sorry that you’re frustrated about this! Maybe if you can show more examples that you find annoying, we can suggest patterns that work better. We’d love to learn your complaints apart from the one in the first post. Thanks!

  5. gaearon commented on Nov 28, 2017

    @gaearon
    Contributor

    We support the above now ^^
    https://reactjs.org/blog/2017/11/28/react-v16.2.0-fragment-support.html

    const welcomeUser = this.state.existingCookie.username &&
      <>
        <div className={CSS.welcomeUser}>
          {i18n.t('Welcome ') + this.state.existingCookie.username}
        </div>
        <div className={CSS.pleaseLogIn}>
          {i18n.t('Please log in')}
        </div>
      </>;

    It will take a while for tools to support <> so you can use <React.Fragment> explicitly in the meantime.

    const welcomeUser = this.state.existingCookie.username &&
      <React.Fragment>
        <div className={CSS.welcomeUser}>
          {i18n.t('Welcome ') + this.state.existingCookie.username}
        </div>
        <div className={CSS.pleaseLogIn}>
          {i18n.t('Please log in')}
        </div>
      </React.Fragment>;

    I hope React is less "stupid" now! 😄

  6. hegelstad commented on Oct 2, 2018

    @hegelstad

    Consider this array of elements that is rendered inside a react component's render function, what is the best practice?

    React complains about unique key prop, but I would rather not pollute my code with those.

    {[
        !hasDashboards && this.props.canCreateDashboard &&
              <EmptyState
                    action={() => this.createDashboard()}
                    actionText="Add dashboard"
                    description="Create your first dashboard" />,
         isEmptyDashboard && this.props.canEditSelectedDashboard &&
              <EmptyState
                    action={() => this.props.setDashboardMode(MODES.ADMIN)}
                    actionText="Add dashboard widgets"
                    description="Start adding to your dashboard" />,
         isEmptyDashboard && !this.props.canEditSelectedDashboard &&
              <EmptyState
                    title="This dashboard is currently empty."
                    description="Ask the admin to add some widgets" />
    ].filter(Boolean)}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions