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
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.
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
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 https://github.com/facebook/react/issues/2127 for some background.
In a future version of JSX, I think we鈥檙e 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鈥檙e frustrated about this! Maybe if you can show more examples that you find annoying, we can suggest patterns that work better. We鈥檇 love to learn your complaints apart from the one in the first post. Thanks!
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! 馃槃
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)}
Most helpful comment
We support the above now ^^
https://reactjs.org/blog/2017/11/28/react-v16.2.0-fragment-support.html
It will take a while for tools to support
<>
so you can use<React.Fragment>
explicitly in the meantime.I hope React is less "stupid" now! 馃槃