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
- Eliminate react key warning in Arc.jsx See https://github.com/facebook/react/issues/2127 Now we are using a <g> element to wrap the returned elements from renderOuterLabels method. — committed to yang-wei/react-d3 by yang-wei 9 years ago
- Move nodes around by reference instead of by index This makes things easier if we ever want to use more than one DOM node for a component. Notably, this is more convenient if we want to remove the wr... — committed to sophiebits/react by sophiebits 8 years ago
- Move nodes around by reference instead of by index This makes things easier if we ever want to use more than one DOM node for a component. Notably, this is more convenient if we want to remove the wr... — committed to sophiebits/react by sophiebits 8 years ago
- Added a <div> as parent element for Then/Else Issue #13 This will wrap siblings without parents in a <div> in order to stop React from giving errors. This issue is still open in React (https://githu... — committed to Borderliner/react-if by Borderliner 8 years ago
- Failing test to demonstrate problem with multiple children `MatchProvider` (and, by extension, `StaticRouter`), can only accept a single child (or a function). The Quick Start has a `BrowserRouter` w... — committed to trabian/react-router by trabianmatt 8 years ago
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 fromrender: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:
😉
From the top:
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:
to end up as the DOM:
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.
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
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:
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.
GridLayoutwould expect to receive a list of children.FadeAnimatorwould intercept that list, inject the appropriate styles and/or event listeners, and return the new list forGridLayoutto consume. There’s no reason forFadeAnimatorto 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, becauseFadeAnimatoris being required to act as a container for its children.With the current limitations, I suppose you could do something like this:
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:
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.
Are you using it successfully in real projects?
@mwiencek
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:
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
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.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 yourrenderfunction only returns one root element / component it should work.OK:
Not OK:
@jonchay You could create a component that only renders its children.
and then to use it:
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 doreturn <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: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:
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?@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
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.mapshould expand fragments. If you want to iterate over every child (including children of child fragments), useChildren.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-componentTo 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 alsobabel-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-componentThe 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”.
(from knockout docs)
It affects also css selectors in the very same way @AdamKyle posted before.