react: Add fragment API to allow returning multiple components from render


Note from maintainers:

We know this is an issue and we know exactly what set of problem can be solved. We want this too but it is a hard problem with our current architecture. Additional comments expressing desire for this feature are not helpful. Feel free to subscribe to the issue (there’s button in the right hand column) but do not comment unless you are adding value to the discussion. “Me too” and “+1” are not valuable, nor are use cases that have already been written in the comments (e.g., we know that you can’t put <tr> or <dd> elements with a <div>).


Consider the following:

var ManagePost = React.createClass({

  render: function() {
    var posts = this.props.posts

    var something;
    var somethingelse;

    var row = posts.map(function(post){
      return(
        <div>
          <div className="col-md-8">
          </div>
          <div className="cold-md-4">
          </div>
        </div>
      )
    });

    return (
        {row}
    );
  }

});

If you remove the <div></div> in the map, you get the following error: Adjacent XJS elements must be wrapped in an enclosing tag

it isn’t till I re-add the surrounding, and rather pointless, divs that it compiles with out issue. I am running 0.11.1

Is this being addressed? It adds extra, and again - IMO - useless and pointless html to the page, that while harming nothing - looks messy and unprofessional. Maybe I am just doing something wrong, please enlighten me if I am.

About this issue

  • Original URL
  • State: closed
  • Created 10 years ago
  • Reactions: 147
  • Comments: 148 (38 by maintainers)

Commits related to this issue

Most upvoted comments

I think we can close this.

Returning arrays from components is supported since React 16 Beta 1 which you can try now.

There are still some limitations (SSR support is not ready) but we’re tracking them in #8854 and will fix before the final 16 release.

Thanks to everyone for your feedback!

Add fragment API to allow returning multiple components from render! Add fragment API to allow returning multiple components from render! Add fragment API to allow returning multiple components from render! Add fragment API to allow returning multiple components from render! Add fragment API to allow returning multiple components from render! Add fragment API to allow returning multiple components from render! Add fragment API to allow returning multiple components from render! Add fragment API to allow returning multiple components from render!

Also, another use case for this - the extra wrapping elements can be problematic when using flexbox.

FYI, the rewrite we’re working on (#6170) already supports fragments. You can track our progress in #7925 and on http://isfiberreadyyet.com.

Thanks all for the words. We know this is hard and still have it on our to-do list. (Asking enthusiastically won’t make it happen sooner @janryWang.)

Just saying… I’m not advocating returning multiple children from component but I’d love to do that in render* methods that I extract from render:

  render: function () {
    return (
      <div className={this.getClassName()}
           style={{
             color: this.props.color,
             backgroundColor: this.props.backgroundColor
           }}>
        {condition ?
          this.renderSomething() :
          this.renderOtherThing()
        }
      </div>
    );
  },

  renderSomething() {
    return (
      <>
        <div className='AboutSection-header'>
          <h1>{this.props.title}</h1>
          {this.props.subtitle &&
            <h4>{this.props.subtitle}</h4>
          }
        </div>,

        {hasChildren &&
          <div className='AboutSection-extra'>
            {this.props.children}
          </div>
        }
      </>
    );
  }

But I should probably just shut up and use keys.

Yes, it supports returning strings too.

@trusktr

As noted in the very first post in this thread:

Note from maintainers:

We know this is an issue and we know exactly what set of problem can be solved. We want this too but it is a hard problem with our current architecture. Additional comments expressing desire for this feature are not helpful.

😉

From the top:

“Me too” and “+1” are not valuable, nor are use cases that have already been written in the comments (e.g., we know that you can’t put <tr> or <dd> elements with a <div>).

fwiw, It’s relatively easy to hack a “fragment” component into React that is treated as an array of its children by React. It will auto-generate keys, but since it happens after the initial validation of components, it won’t throw the usual “no keys supplied” error.

With that said, that hack only solves what @gaearon was talking about above - not having to deal with ugly array syntax/setting arbitrary keys in your JSX - and not the issue of returning multiple nodes at the root of a component’s render method.

I have a problem with the idea that a component needs to return one “element/node”. To me, it seems perfectly reasonable for a JSX structure of:

<Main>
  <Foo />
  <Fragment>
    <Bar />
    <Baz />
  </Fragment>
</Main>

to end up as the DOM:

<div>
  <div>Foo</div>
  <div>Bar</div>
  <div>Baz</div>
</div>

I don’t think this is a principle-of-least-surprise violation because components already do all kinds of “surprising things” with the DOM using lifecycle hooks (just look at the common wormhole pattern). It is generally accepted that a component will not necessarily result in a single element being created, and that’s fine, because some compromises have to be made to work with the DOM.

This isn’t about “optimizations,” either, or even just about not liking array syntax. As many users have mentioned, wrapper elements break styling and layout in serious ways. Tables are the most obvious, but Flexbox is also a major issue. I already have CSS that just reapplies flex rules to wrapper elements that only exist because of React, and it’s pretty ugly.

For all the use-cases presented here I’m pretty sure you could replace <BunchOfComponents /> with {getBunchOfComponents()} and the visual output would be the same, without introducing the practical and technical issues related to having components with fragments as root.

This requires developers to compromise on making isolated, reusable components - heaven help them if they decide they want to reuse their bunch of components elsewhere - because of an underlying implementation issue in React. I don’t think that should be accepted.

THANK YOU DAN

And using a “pseudo” wrapper element, like a HTML comment is not an option? I thought that was the way text nodes where “solved”…

We’ve been using https://github.com/mwiencek/react-packages in production for a few months. It uses this comment wrapper approach, so fragments can be unambiguously nested.

I haven’t seen this mentioned in the thread thus-far, but I think solving this also improves composability. For instance, imagine you have a grid where you want the cells to fade in a certain order. Ideally, there’d be two components at play here: one to handle the layout and another to handle the animation. You’d have an API like this:

<GridLayout
  columns = { 3 }
>
  <FadeAnimator
    springConfig = { springConfig }
  >
    { ...cells }
  </FadeAnimator>
</GridLayout>

This would enable you switch to a different layout, or a different animation, without one having to know about the implementation details of the other. GridLayout would expect to receive a list of children. FadeAnimator would intercept that list, inject the appropriate styles and/or event listeners, and return the new list for GridLayout to consume. There’s no reason for FadeAnimator to be concerned with laying out a grid, except that React elements can’t return Arrays from render. Moreover, there’s no simple way to replace the grid with, say, a masonry layout, because FadeAnimator is being required to act as a container for its children.

With the current limitations, I suppose you could do something like this:

<FadeAnimator
  wrapper = {
    <GridLayout
      columns = { 3 }
    />
  }
  springConfig = { springConfig }
>
  { ...cells }
</FadeAnimator>

// FadeAnimator
render() {
  return React.cloneElement(
    props.wrapper,
    null,
    props.children
  );
}

but that makes the code less clear, more complex, and harder to compose.

The warning is necessary because lack of keys can cause correctness issues, not just performance problems. This is explained in many other issues asking why keys are necessary so I encourage you to search for them and read those discussions. I agree the docs could be clearer about this, and it’s something we’ll likely look at next time we do a docs change sprint.

There is no conceptual differences between arrays directly returned from render, and arrays inside a div. So there is no reason why there would be a key warning in one case but not the other. They need to work the same way because both are affected by the same problems when the keys are missing.

That said we understand it’s annoying to specify keys for static content. Just like you don’t specify keys when you write a JSX structure where children are statically known (and thus never reorder), it would be nice to have a way to do this with arrays.

In the future we might solve this by adding explicit support for fragments in JSX with syntax like:

return (
  <>
    <div>child 1</div>
    <div>child 2</div>
  </>
);

It could produce an array but implicitly assign numeric indexes to the children because in this case we know they can never reorder. This guarantee given by JSX child expression is exactly what lets us get away without specifying keys in normal JSX code where a div might have multiple children.

But we don’t have this syntax yet. So for now this is a known limitation.

We talked more about this in the last team meeting. The consensus is that we don’t want to go with this particular implementation. However this feature will be supported in the long term core rewrite (no timeline on this for now).

We will also consider it again as one of the things we could potentially work on for the second half of this year if the rewrite takes too long or doesn’t work out. No guarantees it will make the list but we will keep you all updated if it comes up.

To get a better idea of what we work on, please see our meeting notes repo! You can find our latest discussion on this in https://github.com/reactjs/core-notes/blob/master/2016-07/july-07.md.

Given that there is a working solution in https://github.com/mwiencek/react-packages

Are you using it successfully in real projects?

@mwiencek

The implementation isn’t really pretty, but if it works for people I can submit a PR and let the React devs tear it apart.

Sure, please send a PR!

@syranide the biggest problem is that you can’t always use a wrapping element in html, like in tables, lists, flexbox, head… Working around that leads to ugly code.

I would be perfectly happy with a virtual wrapper element that only renders comments, as suggested before.

On Fri, May 29, 2015, 3:56 PM Andreas Svensson notifications@github.com wrote:

@syranide https://github.com/syranide but every time one of the components changes all of its siblings need recalculating…

@wmertens https://github.com/wmertens Yes, but many times you would have that anyway because the parent would need to rerender for other reasons, or simply because you receive the data through props anyway. But yes, that is the difference, but it doesn’t mean this approach is right, it’s an optimization and there are many ways to accomplish them.

Also if you use plain coffeescript it’s easy to return an array, so please decouple the functionality from the jsx representation.

That is irrelevant and this is not a problem with JSX. A big issue is that you lose the technical, practical and intuitive assumption of one component = one element/node. I can’t speak for the devs but I would not give up that willingly, it’s a very useful assumption to have. I’m sure there are equally good or better optimizations that could be design if optimization is the only the reason people want this.

— Reply to this email directly or view it on GitHub https://github.com/facebook/react/issues/2127#issuecomment-106810565.

Actually it seems odd to me that we warn about missing keys when all items are strings. I wouldn’t expect us to do so. Is this existing behavior in 15 when strings are inside a div? If not then we should fix it in 16 for top-level strings (since it’s impossible to give keys to them).

I create app with Framework7 and React, however the framework7 html format is fixed,

<div class="page"><div class="navbar"></div><div class="searchbar"></div><div class="page-content"></div><div class="toolbar"></div></div>

I can not wrap the first level child elements (navbar, searchbar) in other div which not having ‘page’ class

@amertak

import React from 'react';
import createFragment from 'react-addons-create-fragment';

let nativeCreateElement = React.createElement;

React.createElement = function() {
  if (arguments[0] !== 'frag') {
    return nativeCreateElement.apply(this, arguments);
  }

  let length = arguments.length;
  if (length <= 2) {
    return null;
  }

  let children = {};
  for (let i = 2; i < length; i++) {
    children['~' + (i - 2)] = arguments[i];
  }

  return createFragment(children);
};

The new algorithm is in the works and supports fragments. However I wouldn’t expect it to become production ready for months to come. I wonder if adding this first to React DOM and later to React Native is too bad? The downside is it fragments the ecosystem a little (pun intended!), but it may give us some time to experiment with that feature. We have a team meeting today so I’ll raise this question if we have time to get a better understanding.

And using a “pseudo” wrapper element, like a HTML comment is not an option? I thought that was the way text nodes where “solved”…

That still doesn’t cover the use case of a conditional with multiple elements. I don’t consider “Use an array and manually add an arbitrary key={...} to each element” to be proper a long term solution.

  render () {
    let user = this.state.user
    let profile = user.get('data')
    let view = null

    if (user.get('status') === 'fetched') {
      view = (
        <h1>{profile.get('login')}</h1>
        <img src={profile.get('avatar_url')} />
        <dl>
          <dt>email</dt>
          <dd>{profile.get('email')}</dd>
        </dl>
      )
    } else if (user.get('status') === 'fetching') {
      view = <h1>fetching</h1>
    } else if (user.get('status') === 'error') {
      view = <h1>{profile.message}</h1>
    }

    return (
      <div className={className}>
        {view}
      </div>
    )
  }

There should at least be a way to return multiple fragments while doing interpolation and “assembly”. The above example is complaining about img and h1 being adiacent but they will end up being inside the main wrapper anyway. That’s one wrapper element I wish I could get rid of.

@gaearon Is there another issue for the <> syntax proposal that we can watch instead of further discussion on this issue? I searched around but I wasn’t able to find one.

Gosh dang it, I gotta stop doing that.

@spicyj actually it is an «issue» since it cause extended energy spendings from developers and there is fundamental possibility to program view framework without such requirement (for example https://github.com/dfilatov/vidom)

Given that there is a working solution in https://github.com/mwiencek/react-packages, is there any chance this will be part of React proper soon? Or are we waiting on the new reconciler?

@KaiStapel This issue is about returning multiple components (or elements I guess) from render(). As long as your render function only returns one root element / component it should work.

OK:

render() {
  return (
    <dl>
      <dt>Def 1</dt>
      <dd>Some description</dd>
      <dt>Def 2</dt>
      <dd>Some other description</dd>
    </dl>
  )
}

Not OK:

render() {
  return (
    <h2>my list</h2>
    <dl>
      <dt>Def 1</dt>
      <dd>Some description</dd>
      <dt>Def 2</dt>
      <dd>Some other description</dd>
    </dl>
  )
}

@jonchay You could create a component that only renders its children.

function statelessWrapper(props) {
   return props.children;
}

and then to use it:

render() {
   return (  
      <statelessWrapper>
         {renderABunchOfComponents()}
      </statelessWrapper>
    );
}

Keys aren’t an “issue”. 😃 They’re a fundamental and necessary part of any view framework that allows you to create dynamic lists.

See https://facebook.github.io/react/tutorial/tutorial.html#keys for a more detailed explanation.

This warning is the exact same as for every other usage of arrays of components, because it boils down to the exact same “extra work” for the reconciler, and thus the exact same potential optimization by setting a key. To me the surprise would be if arrays of components were to be treated differently depending on this, and I imagine that would prompt some Stack Overflow questions too. Not to mention that I think it would involve some overhead just to keep track of it in the first place.

@diligiant returning ['foo', 'bar'] is not relevant to this issue. You never could and still can’t do return <div>{['foo','bar']}</div> Each child in an array should have a ‘key’ prop whether it’s in an internal jsx tag like <div> or it’s being returned. What you can do now is:

return [<div key='1'>foo</div>, <span key='2'>bar</span>];

It removes a big limitation in react.

Btw returning ['foo', 'bar'] gives you a warning not an error and to remove that warning you can easily join those strings or if they’re jsx tags and not strings you can add a key prop to them. So there’s no limitation about returning arrays.

@gaearon It would be interesting with traction on having an official frag-syntax at least.

@GGAlanSmithee Well hardcoded yes, but you can not do:

<dl>
   loop here and print out dt/dd pairs
</dl>

Which is very sad. Same also goes for tables with rowspans, since you can not render two <tr> elements at once 😦

So essentially it is currently not possible to create proper description lists <dl> with React?

<dl>
  <dt>Def 1</dt>
  <dd>Some description</dd>
  <dt>Def 2</dt>
  <dd>Some other description</dd>
</dl>

@isiahmeadows Just a friendly reminder to stay on topic. Feel free to use the mithril gitter room if you want to chat about other topics.

@appsforartists

Perhaps, React.Children.map should expand fragments

Mithril stable does something similar internally (i.e. flattening sub-arrays), but I’m moving away from that model due to perf reasons (and also due to some historical headaches regarding vnode lists that mixed keyed and non-keyed nodes). Might be something to consider.

@Primajin I’ve wondered that too, but I suspect they’d be passed as one element. It’s important to make fragments composable (see my example above). However, there are probably times you’d want to treat them as one unit too.

Perhaps, React.Children.map should expand fragments. If you want to iterate over every child (including children of child fragments), use Children.map. If you want to treat fragments as an opaque box, work directly with props.children as an {array, element}.

To handle updates correctly I figured the solution @spicyj thought of for #5753 might work for fragments too (wrapping the contents in something like <!-- react-frag: 1 --><!-- /react-frag: 1 -->). Yeah the comments are a bit ugly, but it’s way more reliable than what I was trying to do with _nestedChildCount. That approach is now what’s used at https://github.com/mwiencek/react/tree/frag-component

To build and expand on the last comment of @syranide, there seems to be no need for an extra “Fragment API” if render allows for arrays as return value. JSX could transform multiple root elements into an array, which would also work for return values of any other function. So instead of introducing additional API surface, which requires documentation and learning, one of React’s limitations could just be removed.

This would at least affect babel-plugin-transform-react-jsx (implementation) and also babel-plugin-syntax-jsx(removing parse error for adjacent root elements). While changing the former seems to be fairly safe, I don’t know the scope / usage of the latter and the impact the proposed change would have on other projects.

I need this feature for already-stated reasons, so tried implementing a <frag></frag> container at https://github.com/mwiencek/react/tree/frag-component

The implementation isn’t really pretty, but if it works for people I can submit a PR and let the React devs tear it apart.

@whatknight That won’t work except in cases that return renderABunchOfComponents(); already works.

If you have many small components (ie design very modularly), you end up having to wrap all kinds of things in divs that shouldn’t be. I might be conflating, but I think this relates to this issue.

In case it’s useful: @Prinzhorn’s suggestion of using HTML comments to map “fragment components” to the DOM is the same approach that Knockout uses. Knockout calls these “virtual elements”.

<!-- ko component: "message-editor" -->
<!-- /ko -->

(from knockout docs)

It affects also css selectors in the very same way @AdamKyle posted before.