Description
This is a feature request.
It is a common request from developers to allow returning multiple elements from inline expressions in JSX.
A use case for this would be this pseudocode:
<Parent>{ flag ? <Child/> : <Sibling1/><Sibling2/><Sibling3/>}</Parent>
which would have React generate either <Parent><Child/></Parent>
or <Parent><Sibling1/><Sibling2/><Sibling3/></Parent>
, depending on the value of the flag.
As it stands now, this would not work, as two or more consecutive tags would not transpile to a single expression.
There are several approaches to that problem:
-
Wrap the elements into some kind of a parent tag:
<Parent>{ flag ? <Child/> : <DummyTag><Sibling1/><Sibling2/><Sibling3/></DummyTag>}</Parent>
This is not always acceptable.
-
Use arrays:
<Parent>{ flag ? [<Child/>] : [<Sibling1/><Sibling2/><Sibling3/>]}</Parent>
This would make React complain on the elements in the second array having no unique
key
property, and adding that property would take some extra effort. -
Use keyed fragments (Add fragment API to allow returning multiple components from render react#2127)
<Parent>{ flag ? <Child/> : React.createFragment({ sibling1: <Sibling1/>, sibling2: <Sibling2/>, sibling3: <Sibling3/>})}</Parent>
This requires assigning arbitrary keys to the fragments, the syntax is quite cumbersome and it's not JSX.
-
Use
createElement
directly, making benefit of ES6 spread syntax:React.createElement('parent', {}, ...(flag ? [<Child/>] : [<Sibling1/><Sibling2/><Sibling3/>]))
This is the most straightforward way, as it would be equivalent to either
React.createElement('parent', {}, <Child/>)
orReact.createElement('parent', {}, <Sibling1/>, <Sibling2/>, <Sibling3/>)
, but it's not JSX either.
My proposal is to make use of the latter solution in JSX, extending JSX with a Spread tag: <...>
This tag could only be used as a non-top element in a JSX tree and could only contain a single JS expression in curly braces, which must evaluate to an array.
This Spread tag <...>{ expression }</...>
would transpile to ES6 array spread syntax: ...(expression)
, so that <Parent><a/><...>{ expression }</...><b/></Parent>
becomes React.createElement('parent', {}, React.createElement('a', {}), ...(expression), React.createElement('b', '{}'))
This way, the original problem could be solved by writing:
<Parent/><...>{ flag ? [<Child/>] : [<Sibling1>, <Sibling2>, <Sibling3>]}</...><Parent/>
which would transpile as follows:
React.createElement('parent', {},
...(flag ?
[React.createElement('child', {})] :
[React.createElement('sibling1', {}),
React.createElement('sibling2', {}),
React.createElement('sibling3', {})]
)
);
This is almost as simple as the naive solution at the very top, and it produces the exact effect most developers are after: variable number of arguments to createElement
with no need to create keys or wrap elements into dummy tags.
Also, it's a pure JSX feature which does not introduce any new React code.