Skip to content

JSX Spread tag proposal #63

Closed
Closed
@quassnoi

Description

@quassnoi

This is a feature request.

It is a common request from developers to allow returning multiple elements from inline expressions in JSX.

http://stackoverflow.com/questions/23840997/how-to-return-multiple-lines-jsx-in-another-return-statement-in-react

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:

  1. Wrap the elements into some kind of a parent tag:

    <Parent>{ flag ? <Child/> : <DummyTag><Sibling1/><Sibling2/><Sibling3/></DummyTag>}</Parent>
    

    This is not always acceptable.

  2. 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.

  3. 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.

  4. 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/>) or React.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.

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