New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add fragment API to allow returning multiple components from render #2127

Closed
AdamKyle opened this Issue Sep 2, 2014 · 148 comments

Comments

Projects
None yet
@AdamKyle

AdamKyle commented Sep 2, 2014


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.

@chenglou

This comment has been minimized.

Show comment
Hide comment
@chenglou

chenglou Sep 2, 2014

Contributor

Because when you don't put the wrapper, it desugars to this:

return React.DOM.div(...)React.DOM.div(...)

Which doesn't make syntactical sense. The jsx compiler page might help if you need a visual mapping.

That being said, it's possible to desugar this to [div, div] instead. This is hard, somewhat controversial and won't be implemented in a near future.

Contributor

chenglou commented Sep 2, 2014

Because when you don't put the wrapper, it desugars to this:

return React.DOM.div(...)React.DOM.div(...)

Which doesn't make syntactical sense. The jsx compiler page might help if you need a visual mapping.

That being said, it's possible to desugar this to [div, div] instead. This is hard, somewhat controversial and won't be implemented in a near future.

@sophiebits

This comment has been minimized.

Show comment
Hide comment
@sophiebits

sophiebits Sep 2, 2014

Member

(I don't think it's particularly controversial but it does add code complexity and hasn't been done yet.)

Member

sophiebits commented Sep 2, 2014

(I don't think it's particularly controversial but it does add code complexity and hasn't been done yet.)

@chenglou

This comment has been minimized.

Show comment
Hide comment
@chenglou

chenglou Sep 2, 2014

Contributor

IIRC @syranide has a few comments on this

Contributor

chenglou commented Sep 2, 2014

IIRC @syranide has a few comments on this

@syranide

This comment has been minimized.

Show comment
Hide comment
@syranide

syranide Sep 2, 2014

Contributor

@chenglou Hehe.

I had a brief chat about it with chenglou a little while ago, I don't really lean one way or the other at the moment. I see a lot of hidden dangers in allowing a composite components to return multiple components and it breaks a lot of intuitive assumptions, but I don't know of any (right now) good practice use-cases that would obviously benefit from this.

The simplicity of returning at most one component means that it's very easy to reason about what you see, if not, <table><TableHeader /></table> could actually render any number of rows, you have no way of knowing other than inspecting TableHeader and whatever composite components it returns.

I feel like being able to return multiple components only moves the responsibility of wrapping components as necessary from the "composite component" to whatever "renders the composite component". The component "that renders composite component" rarely has or should have the knowledge of whether to wrap composite components or not, whereas children are more likely to have knowledge of their parents.

But perhaps it's simply a case of developer responsibility. There may be good use-cases for both and we should just look past the inevitable misuse.

@sebmarkbage Probably has a few comments too :)

Contributor

syranide commented Sep 2, 2014

@chenglou Hehe.

I had a brief chat about it with chenglou a little while ago, I don't really lean one way or the other at the moment. I see a lot of hidden dangers in allowing a composite components to return multiple components and it breaks a lot of intuitive assumptions, but I don't know of any (right now) good practice use-cases that would obviously benefit from this.

The simplicity of returning at most one component means that it's very easy to reason about what you see, if not, <table><TableHeader /></table> could actually render any number of rows, you have no way of knowing other than inspecting TableHeader and whatever composite components it returns.

I feel like being able to return multiple components only moves the responsibility of wrapping components as necessary from the "composite component" to whatever "renders the composite component". The component "that renders composite component" rarely has or should have the knowledge of whether to wrap composite components or not, whereas children are more likely to have knowledge of their parents.

But perhaps it's simply a case of developer responsibility. There may be good use-cases for both and we should just look past the inevitable misuse.

@sebmarkbage Probably has a few comments too :)

@sebmarkbage

This comment has been minimized.

Show comment
Hide comment
@sebmarkbage

sebmarkbage Sep 2, 2014

Member

We will probably never allow that syntax implicitly. You would need a wrapper like

<>
  <div className="col-md-8">
  </div>
  <div className="cold-md-4">
  </div>
</>

OR

[
  <div className="col-md-8">
  </div>,
  <div className="cold-md-4">
  </div>
]

However, even this doesn't work. Often, it's probably for the best. It can be confusing for consuming components when a child might expand into multiple elements.

But, really, the only reason we don't support this right now is because it's hard to implement. Hopefully, we'll be able to support it sometime in the future but probably not short term. Sorry. :/

Member

sebmarkbage commented Sep 2, 2014

We will probably never allow that syntax implicitly. You would need a wrapper like

<>
  <div className="col-md-8">
  </div>
  <div className="cold-md-4">
  </div>
</>

OR

[
  <div className="col-md-8">
  </div>,
  <div className="cold-md-4">
  </div>
]

However, even this doesn't work. Often, it's probably for the best. It can be confusing for consuming components when a child might expand into multiple elements.

But, really, the only reason we don't support this right now is because it's hard to implement. Hopefully, we'll be able to support it sometime in the future but probably not short term. Sorry. :/

@AdamKyle

This comment has been minimized.

Show comment
Hide comment
@AdamKyle

AdamKyle Sep 3, 2014

can this not affect things like jquery or other libraries that target specific elements, so if you do something like $('#id-name').children(), the following:

<div id="id-name">
  <div>
    <div class="class-name">
    </div>
  </div>
</div>

the <div> and <div class="class-name"> would be selected in this case. (If I understand this properly)

AdamKyle commented Sep 3, 2014

can this not affect things like jquery or other libraries that target specific elements, so if you do something like $('#id-name').children(), the following:

<div id="id-name">
  <div>
    <div class="class-name">
    </div>
  </div>
</div>

the <div> and <div class="class-name"> would be selected in this case. (If I understand this properly)

@DAddYE

This comment has been minimized.

Show comment
Hide comment
@DAddYE

DAddYE Oct 13, 2014

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

DAddYE commented Oct 13, 2014

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

@srph

This comment has been minimized.

Show comment
Hide comment
@srph

srph Nov 16, 2014

Contributor

Any updates on this issue?

Contributor

srph commented Nov 16, 2014

Any updates on this issue?

@gabssnake

This comment has been minimized.

Show comment
Hide comment
@gabssnake

gabssnake Dec 1, 2014

Spent a couple of minutes understanding why my component didn’t work. I feel like there should be a notice somewhere, maybe I missed it ? Maybe it is obviously wrong to try :

var Optimistic = React.createClass({
  render: function() {
    return ( 
      <h1>{this.props.name} loves React</h1>
      <p>React doesn’t. Idea: sprinkle some divs here and there.</p>
    );
  }
});

React.render(
  <Optimistic name="Peter" />,
  document.getElementById('myContainer')
);

gabssnake commented Dec 1, 2014

Spent a couple of minutes understanding why my component didn’t work. I feel like there should be a notice somewhere, maybe I missed it ? Maybe it is obviously wrong to try :

var Optimistic = React.createClass({
  render: function() {
    return ( 
      <h1>{this.props.name} loves React</h1>
      <p>React doesn’t. Idea: sprinkle some divs here and there.</p>
    );
  }
});

React.render(
  <Optimistic name="Peter" />,
  document.getElementById('myContainer')
);
@sophiebits

This comment has been minimized.

Show comment
Hide comment
@sophiebits

sophiebits Dec 1, 2014

Member

@gabssnake You should have gotten a JSX compile error with the error "Adjacent XJS elements must be wrapped in an enclosing tag"; did you not see the error or was it not clear in its explanation?

Member

sophiebits commented Dec 1, 2014

@gabssnake You should have gotten a JSX compile error with the error "Adjacent XJS elements must be wrapped in an enclosing tag"; did you not see the error or was it not clear in its explanation?

@gabssnake

This comment has been minimized.

Show comment
Hide comment
@gabssnake

gabssnake Dec 1, 2014

Thank you for your answer @spicyj. Well, I meant a notice in the React documentation. Yes, the console did show an error, but the need to wrap didn’t make sense to me at first. That is why I searched and got here.

gabssnake commented Dec 1, 2014

Thank you for your answer @spicyj. Well, I meant a notice in the React documentation. Yes, the console did show an error, but the need to wrap didn’t make sense to me at first. That is why I searched and got here.

@GetContented

This comment has been minimized.

Show comment
Hide comment
@GetContented

GetContented Jan 28, 2015

I've had this pain as well... particularly painful for my designer, as a matter of fact. It'd be kind of nice if a component could output a node (therefore node-list or fragment) instead of an element.

GetContented commented Jan 28, 2015

I've had this pain as well... particularly painful for my designer, as a matter of fact. It'd be kind of nice if a component could output a node (therefore node-list or fragment) instead of an element.

@gaearon

This comment has been minimized.

Show comment
Hide comment
@gaearon

gaearon Jan 28, 2015

Member

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.

Member

gaearon commented Jan 28, 2015

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.

@syranide

This comment has been minimized.

Show comment
Hide comment
@syranide

syranide Jan 28, 2015

Contributor

@gaearon You can do that already though, you just need to return an array for now (which is a bit cumbersome though yes) ... buuuuut, you can work around that, I have hacked my own <Frag> component which is translated to an array (overloaded React.render) ... you could also do return <NoopComp>...</NoopComp>.props.children I assume, if you want to avoid hacks.

EDIT: My bad, I overloaded React.createElement not React.render.

Contributor

syranide commented Jan 28, 2015

@gaearon You can do that already though, you just need to return an array for now (which is a bit cumbersome though yes) ... buuuuut, you can work around that, I have hacked my own <Frag> component which is translated to an array (overloaded React.render) ... you could also do return <NoopComp>...</NoopComp>.props.children I assume, if you want to avoid hacks.

EDIT: My bad, I overloaded React.createElement not React.render.

@gaearon

This comment has been minimized.

Show comment
Hide comment
@gaearon

gaearon Jan 28, 2015

Member

The problem with arrays is they trip our designer. Need commas, explicit keys.

Member

gaearon commented Jan 28, 2015

The problem with arrays is they trip our designer. Need commas, explicit keys.

@syranide

This comment has been minimized.

Show comment
Hide comment
@syranide

syranide Jan 28, 2015

Contributor

@gaearon Yeah you can avoid the commas using either of my two workarounds for now (if you find either acceptable)... but what you do mean with explicit keys?

Contributor

syranide commented Jan 28, 2015

@gaearon Yeah you can avoid the commas using either of my two workarounds for now (if you find either acceptable)... but what you do mean with explicit keys?

@gaearon

This comment has been minimized.

Show comment
Hide comment
@gaearon

gaearon Jan 29, 2015

Member

If I use array syntax, I need to specify key on each element. Not that it's hard to do, but feels awkward because I know they never change.

Member

gaearon commented Jan 29, 2015

If I use array syntax, I need to specify key on each element. Not that it's hard to do, but feels awkward because I know they never change.

@syranide

This comment has been minimized.

Show comment
Hide comment
@syranide

syranide Jan 29, 2015

Contributor

@gaearon Ah yes, I choose to just mentally ignore that warning for now :), if you really want to to avoid it, you can do <MyComp children={this.renderWhatever()} /> to avoid it (EDIT: although you obviously can't use that if you have adjacent children, you could use some flattening helper... but yeah).

Contributor

syranide commented Jan 29, 2015

@gaearon Ah yes, I choose to just mentally ignore that warning for now :), if you really want to to avoid it, you can do <MyComp children={this.renderWhatever()} /> to avoid it (EDIT: although you obviously can't use that if you have adjacent children, you could use some flattening helper... but yeah).

@natew

This comment has been minimized.

Show comment
Hide comment
@natew

natew Feb 11, 2015

Another case I've ran into with a UI kit. When you are placing children inside a fixed scrollable container like so:

return (
  <div style={{
    position: fixed
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    overflow-y: scroll;
  }}>
    {this.props.children}
  </div>
);

The children now must be passed as an array, but if a composite class is passed, it must also implement those styles to avoid breaking it. Just adds a layer of complexity. But I can completely understand how complex it must be to make the change. Just throwing my hat in for support.

natew commented Feb 11, 2015

Another case I've ran into with a UI kit. When you are placing children inside a fixed scrollable container like so:

return (
  <div style={{
    position: fixed
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    overflow-y: scroll;
  }}>
    {this.props.children}
  </div>
);

The children now must be passed as an array, but if a composite class is passed, it must also implement those styles to avoid breaking it. Just adds a layer of complexity. But I can completely understand how complex it must be to make the change. Just throwing my hat in for support.

@sophiebits sophiebits changed the title from Why is it the divs them selves must be wrapped in divs? to Add fragment API to allow returning multiple components from render Feb 27, 2015

@Prinzhorn

This comment has been minimized.

Show comment
Hide comment
@Prinzhorn

Prinzhorn Mar 15, 2015

I have another use-case I described in detail over here #3415. But I can workaround it for now and understand that it is hard to implement and rare.

I know nothing about react internals (yet), but I'll just throw in an idea how you could mark such virtual parent elements/fragments in the DOM: comments. E.g.

<A>
    <B></B>
    <Fragment>
        <C></C>
        <D></D>
    </Fragment>
    <E></E>
</A>

would render to

<a>
    <b></b>
    <!--<fragment data-reactid="">-->
        <c></c>
        <d></d>
    <!--</fragment>-->
    <e></e>
</a>

This means c and d will be treated as children of sth. (including the nested reactid to not conflict with b and e). I've seen other frameworks "misuse" comments for these kind of semantical jobs.

Prinzhorn commented Mar 15, 2015

I have another use-case I described in detail over here #3415. But I can workaround it for now and understand that it is hard to implement and rare.

I know nothing about react internals (yet), but I'll just throw in an idea how you could mark such virtual parent elements/fragments in the DOM: comments. E.g.

<A>
    <B></B>
    <Fragment>
        <C></C>
        <D></D>
    </Fragment>
    <E></E>
</A>

would render to

<a>
    <b></b>
    <!--<fragment data-reactid="">-->
        <c></c>
        <d></d>
    <!--</fragment>-->
    <e></e>
</a>

This means c and d will be treated as children of sth. (including the nested reactid to not conflict with b and e). I've seen other frameworks "misuse" comments for these kind of semantical jobs.

@GetContented

This comment has been minimized.

Show comment
Hide comment
@GetContented

GetContented Mar 15, 2015

@Prinzhorn I think you might be confusing DOM output with React's JSX.

GetContented commented Mar 15, 2015

@Prinzhorn I think you might be confusing DOM output with React's JSX.

@aldendaniels

This comment has been minimized.

Show comment
Hide comment
@aldendaniels

aldendaniels Apr 6, 2015

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)

aldendaniels commented Apr 6, 2015

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)

@aldendaniels

This comment has been minimized.

Show comment
Hide comment
@aldendaniels

aldendaniels Apr 6, 2015

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

aldendaniels commented Apr 6, 2015

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

@natew

This comment has been minimized.

Show comment
Hide comment
@natew

natew Apr 6, 2015

@aldendaniels absolutely agree, I've ran into flexbox issues already.

natew commented Apr 6, 2015

@aldendaniels absolutely agree, I've ran into flexbox issues already.

@brigand

This comment has been minimized.

Show comment
Hide comment
@brigand

brigand Apr 20, 2015

Contributor

I ran into this with flexbox with dependent dropdowns. Where A would render and manage the first dropdown, followed by B or C or D depending on the value of A's dropdown, which would each render the appropriate number of dropdowns, e.g.

<A>
 <Drop />
 <C><Drop /><Drop /></C>
</A>

A, B, C, and D are stateless so I changed them from class B {render(){ this.props }} to function B(props){ return [...]; }.

In this case, it doesn't much matter to the parent that multiple children are rendered, it's just to deal with my CSS. There are things I'd do differently if these didn't need to be reused (another component needs B, C, and D).


As an alternative, maybe a way to unravel it from the parent? I don't have any specific ideas of how that'd look.

Contributor

brigand commented Apr 20, 2015

I ran into this with flexbox with dependent dropdowns. Where A would render and manage the first dropdown, followed by B or C or D depending on the value of A's dropdown, which would each render the appropriate number of dropdowns, e.g.

<A>
 <Drop />
 <C><Drop /><Drop /></C>
</A>

A, B, C, and D are stateless so I changed them from class B {render(){ this.props }} to function B(props){ return [...]; }.

In this case, it doesn't much matter to the parent that multiple children are rendered, it's just to deal with my CSS. There are things I'd do differently if these didn't need to be reused (another component needs B, C, and D).


As an alternative, maybe a way to unravel it from the parent? I don't have any specific ideas of how that'd look.

@jeffhandley

This comment has been minimized.

Show comment
Hide comment
@jeffhandley

jeffhandley Apr 20, 2015

I hit a scenario today that I think is a good use case for this feature: A component that renders multiple <script> elements, where it could be rendering into the <head> element of the page. Any wrapper element there would be bad.

My scenario is wanting to have a component that is responsible for rendering the <script> tag both for the runtime code needed on the page as well as another <script> tag that carries the localized strings to be used by the runtime code. For example:

<html>
    <head>
        <script language="runtime.resources.en-us.js"></script>
        <script language="runtime.js"></script>
    </head>
    <body>
    ...
    </body>
</html>

In this case, I'd like to have the code authored as:

var RuntimeScripts = require('./Runtime')
...
return (
    <html>
        <head>
            <RuntimeScripts language="en-us" />
        </head>
    </html>
)
...

jeffhandley commented Apr 20, 2015

I hit a scenario today that I think is a good use case for this feature: A component that renders multiple <script> elements, where it could be rendering into the <head> element of the page. Any wrapper element there would be bad.

My scenario is wanting to have a component that is responsible for rendering the <script> tag both for the runtime code needed on the page as well as another <script> tag that carries the localized strings to be used by the runtime code. For example:

<html>
    <head>
        <script language="runtime.resources.en-us.js"></script>
        <script language="runtime.js"></script>
    </head>
    <body>
    ...
    </body>
</html>

In this case, I'd like to have the code authored as:

var RuntimeScripts = require('./Runtime')
...
return (
    <html>
        <head>
            <RuntimeScripts language="en-us" />
        </head>
    </html>
)
...
@Axbon

This comment has been minimized.

Show comment
Hide comment
@Axbon

Axbon Apr 30, 2015

I also ran into some flexbox issues. Nothing that can't be solved in CSS, but one of the "beauties" of flexbox is that you need less "wrapper" elements everywhere to make your layout work, but you'll still end up with wrapper elements everywhere when using React since you always wrap whatever you return in div/div or similar, unless it makes sense to have a container.

Axbon commented Apr 30, 2015

I also ran into some flexbox issues. Nothing that can't be solved in CSS, but one of the "beauties" of flexbox is that you need less "wrapper" elements everywhere to make your layout work, but you'll still end up with wrapper elements everywhere when using React since you always wrap whatever you return in div/div or similar, unless it makes sense to have a container.

yang-wei added a commit to yang-wei/react-d3 that referenced this issue May 6, 2015

Eliminate react key warning in Arc.jsx
See facebook/react#2127

Now we are using a <g> element to wrap the returned elements
from renderOuterLabels method.
@syranide

This comment has been minimized.

Show comment
Hide comment
@syranide

syranide May 29, 2015

Contributor

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.

Contributor

syranide commented May 29, 2015

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.

@wmertens

This comment has been minimized.

Show comment
Hide comment
@wmertens

wmertens May 29, 2015

Contributor

@syranide but every time one of the components changes all of its siblings need recalculating...

Contributor

wmertens commented May 29, 2015

@syranide but every time one of the components changes all of its siblings need recalculating...

@wmertens

This comment has been minimized.

Show comment
Hide comment
@wmertens

wmertens May 29, 2015

Contributor

Also if you use plain coffeescript it's easy to return an array, so please decouple the functionality from the jsx representation.
IOW if it's easy to handle a returned array of elements, don't wait for jsx to catch up.

Contributor

wmertens commented May 29, 2015

Also if you use plain coffeescript it's easy to return an array, so please decouple the functionality from the jsx representation.
IOW if it's easy to handle a returned array of elements, don't wait for jsx to catch up.

@gaearon

This comment has been minimized.

Show comment
Hide comment
@gaearon

gaearon Jul 26, 2017

Member

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!

Member

gaearon commented Jul 26, 2017

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!

@gaearon gaearon closed this Jul 26, 2017

@facebook facebook unlocked this conversation Jul 26, 2017

@sophiebits

This comment has been minimized.

Show comment
Hide comment
@sophiebits

sophiebits Jul 26, 2017

Member

THANK YOU DAN

Member

sophiebits commented Jul 26, 2017

THANK YOU DAN

@faceyspacey

This comment has been minimized.

Show comment
Hide comment
@faceyspacey

faceyspacey Jul 26, 2017

🍾🍾🍾

faceyspacey commented Jul 26, 2017

🍾🍾🍾

@kilianc

This comment has been minimized.

Show comment
Hide comment
@kilianc

kilianc commented Jul 26, 2017

🦏

@zheeeng

This comment has been minimized.

Show comment
Hide comment
@zheeeng

zheeeng Jul 27, 2017

@gaearon Awesome improvement! Is it support pure text node?

zheeeng commented Jul 27, 2017

@gaearon Awesome improvement! Is it support pure text node?

@gaearon

This comment has been minimized.

Show comment
Hide comment
@gaearon

gaearon Jul 27, 2017

Member

Yes, it supports returning strings too.

Member

gaearon commented Jul 27, 2017

Yes, it supports returning strings too.

@mhils mhils referenced this issue Aug 7, 2017

Merged

[WIP] [web] Static viewer for mitmweb #2504

4 of 4 tasks complete
@diligiant

This comment has been minimized.

Show comment
Hide comment
@diligiant

diligiant Aug 15, 2017

May I ask how is this supposed to work?

I was hoping it would allow us to render 2 adjacent XJS elements but if I do return ["foo", "bar"] (something more useful ;), I get the expected bundle.js:66656 Warning: Each child in an array or iterator should have a unique "key" prop.

So was the feature a way not to surround a real list by an extraneous element?

diligiant commented Aug 15, 2017

May I ask how is this supposed to work?

I was hoping it would allow us to render 2 adjacent XJS elements but if I do return ["foo", "bar"] (something more useful ;), I get the expected bundle.js:66656 Warning: Each child in an array or iterator should have a unique "key" prop.

So was the feature a way not to surround a real list by an extraneous element?

@JesperTreetop

This comment has been minimized.

Show comment
Hide comment
@JesperTreetop

JesperTreetop Aug 15, 2017

So was the feature a way not to surround a real list by an extraneous element?

Yes, a way to provide several elements from one render. (For more information, see the initial issue post.)

If all your array items are known to be strings, just join them and return a single string. If not, you can either eat this warning or work out what to do to wrap them in elements so that they can be keyed to improve DOM reconciliation, just as you would have if you had included an array of strings inside another element in JSX.

JesperTreetop commented Aug 15, 2017

So was the feature a way not to surround a real list by an extraneous element?

Yes, a way to provide several elements from one render. (For more information, see the initial issue post.)

If all your array items are known to be strings, just join them and return a single string. If not, you can either eat this warning or work out what to do to wrap them in elements so that they can be keyed to improve DOM reconciliation, just as you would have if you had included an array of strings inside another element in JSX.

@sassanh

This comment has been minimized.

Show comment
Hide comment
@sassanh

sassanh Aug 15, 2017

Contributor

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

Contributor

sassanh commented Aug 15, 2017

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

@diligiant

This comment has been minimized.

Show comment
Hide comment
@diligiant

diligiant Aug 15, 2017

@JesperTreetop thank you.
@sassanh, from your example, it seems that I've been too "shy" to use keys "just" to avoid a surrounding (semantically) useless <div>. Do you mean I should go ahead and that there is no real performance penalty? That would indeed be a REAL improvement!

diligiant commented Aug 15, 2017

@JesperTreetop thank you.
@sassanh, from your example, it seems that I've been too "shy" to use keys "just" to avoid a surrounding (semantically) useless <div>. Do you mean I should go ahead and that there is no real performance penalty? That would indeed be a REAL improvement!

@sassanh

This comment has been minimized.

Show comment
Hide comment
@sassanh

sassanh Aug 15, 2017

Contributor

@diligiant I doubt it introduces any performance penalty but you don't need a surrounding useless div unless it's surrounding strings and if it's surrounding strings you can avoid array at all and join strings. If it's not strings you can add the key to the component. For example if you have two components Foo and Bar you can return [<Foo key='foo'/>, <Bar key='bar'/>] and you don't need to surround neither your components (like always) nor your array (thanks to this new release, you needed to surround the array before 16).

Contributor

sassanh commented Aug 15, 2017

@diligiant I doubt it introduces any performance penalty but you don't need a surrounding useless div unless it's surrounding strings and if it's surrounding strings you can avoid array at all and join strings. If it's not strings you can add the key to the component. For example if you have two components Foo and Bar you can return [<Foo key='foo'/>, <Bar key='bar'/>] and you don't need to surround neither your components (like always) nor your array (thanks to this new release, you needed to surround the array before 16).

@diligiant

This comment has been minimized.

Show comment
Hide comment
@diligiant

diligiant Aug 15, 2017

@sassanh cool then. Of course my use case was with multiple components. Happy!! ;)

diligiant commented Aug 15, 2017

@sassanh cool then. Of course my use case was with multiple components. Happy!! ;)

@gaearon

This comment has been minimized.

Show comment
Hide comment
@gaearon

gaearon Aug 15, 2017

Member

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

Member

gaearon commented Aug 15, 2017

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

@diligiant

This comment has been minimized.

Show comment
Hide comment
@diligiant

diligiant Aug 15, 2017

@gaearon sorry, my example was misleading: I meant components.

I checked and under -beta.5, React doesn't issue a warning when rendering an array with multiple strings.

Nonetheless, I am puzzled by the key requirement when rendering an array just for the sake of avoiding a surrounding component. I understand and it's nothing but it will probably raise tons of questions on SO (I have nothing better to offer though…)

And finally, thank you again.

diligiant commented Aug 15, 2017

@gaearon sorry, my example was misleading: I meant components.

I checked and under -beta.5, React doesn't issue a warning when rendering an array with multiple strings.

Nonetheless, I am puzzled by the key requirement when rendering an array just for the sake of avoiding a surrounding component. I understand and it's nothing but it will probably raise tons of questions on SO (I have nothing better to offer though…)

And finally, thank you again.

@JesperTreetop

This comment has been minimized.

Show comment
Hide comment
@JesperTreetop

JesperTreetop Aug 15, 2017

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.

JesperTreetop commented Aug 15, 2017

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.

@zwily

This comment has been minimized.

Show comment
Hide comment
@zwily

zwily Aug 15, 2017

.. and most of the time I will be returning an array from a function, I do want the key warning in place. It seems the times you wouldn't want the warning are the minority, and there's no way for React to know if it's appropriate to not warn.

zwily commented Aug 15, 2017

.. and most of the time I will be returning an array from a function, I do want the key warning in place. It seems the times you wouldn't want the warning are the minority, and there's no way for React to know if it's appropriate to not warn.

@gaearon

This comment has been minimized.

Show comment
Hide comment
@gaearon

gaearon Aug 15, 2017

Member

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.

Member

gaearon commented Aug 15, 2017

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.

@diligiant

This comment has been minimized.

Show comment
Hide comment
@diligiant

diligiant Aug 15, 2017

@JesperTreetop & @zwily, @gaearon explained it way better than I did ;)

Once you know, it's no big deal but as we all want React to thrive, I was just saying…

diligiant commented Aug 15, 2017

@JesperTreetop & @zwily, @gaearon explained it way better than I did ;)

Once you know, it's no big deal but as we all want React to thrive, I was just saying…

@smrq

This comment has been minimized.

Show comment
Hide comment
@smrq

smrq Aug 15, 2017

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

smrq commented Aug 15, 2017

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

@veged

This comment has been minimized.

Show comment
Hide comment
@veged

veged Aug 15, 2017

@smrq +1 to question — I follow everything about those awkward limitations (one-to-one rendrer() result, keys and JSX syntax or fragments) but the only ticket I know is facebook/jsx#65

I also suppose that fibers gonna fix issue with keys at all — but looks like it's unfulfilled dream

veged commented Aug 15, 2017

@smrq +1 to question — I follow everything about those awkward limitations (one-to-one rendrer() result, keys and JSX syntax or fragments) but the only ticket I know is facebook/jsx#65

I also suppose that fibers gonna fix issue with keys at all — but looks like it's unfulfilled dream

@sophiebits

This comment has been minimized.

Show comment
Hide comment
@sophiebits

sophiebits Aug 15, 2017

Member

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.

Member

sophiebits commented Aug 15, 2017

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.

@veged

This comment has been minimized.

Show comment
Hide comment
@veged

veged Aug 15, 2017

@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)

veged commented Aug 15, 2017

@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)

@goto-bus-stop

This comment has been minimized.

Show comment
Hide comment
@goto-bus-stop

goto-bus-stop Aug 15, 2017

there is fundamental possibility to program view framework without such requirement (for example https://github.com/dfilatov/vidom)

vidom does use keys in collections though. It might technically work without it but it would likely be much slower. React could also technically work without keys, but it'd be quite unexpected to find that half of your components have to update when you remove a single item from a list. With keys, only one item is unmounted.

goto-bus-stop commented Aug 15, 2017

there is fundamental possibility to program view framework without such requirement (for example https://github.com/dfilatov/vidom)

vidom does use keys in collections though. It might technically work without it but it would likely be much slower. React could also technically work without keys, but it'd be quite unexpected to find that half of your components have to update when you remove a single item from a list. With keys, only one item is unmounted.

@veged

This comment has been minimized.

Show comment
Hide comment
@veged

veged Aug 15, 2017

@goto-bus-stop vidom can use keys, but they are not required and without them only really big cases with a lot of updates can cause real performance problems

so I consider this part as possible optional (as, for example, shouldComponentUpdate) which can be used for ferformance tweaks in individual cases

veged commented Aug 15, 2017

@goto-bus-stop vidom can use keys, but they are not required and without them only really big cases with a lot of updates can cause real performance problems

so I consider this part as possible optional (as, for example, shouldComponentUpdate) which can be used for ferformance tweaks in individual cases

@brigand

This comment has been minimized.

Show comment
Hide comment
@brigand

brigand Aug 15, 2017

Contributor

@veged example of vidom struggling without keys.

It has no idea it's supposed to reorder the elements, so it discards the instances on each render of the root component.

Contributor

brigand commented Aug 15, 2017

@veged example of vidom struggling without keys.

It has no idea it's supposed to reorder the elements, so it discards the instances on each render of the root component.

@leeoniya

This comment has been minimized.

Show comment
Hide comment
@leeoniya

leeoniya Aug 15, 2017

as someone who's quite familiar with the virtual-dom space [1]. i can say that:

  1. keys are necessary for predictable dom & state updates amongst similar siblings.
  2. keys are typically not an optimization, and - in fact - are usually the opposite.

[1] https://github.com/leeoniya/domvm

leeoniya commented Aug 15, 2017

as someone who's quite familiar with the virtual-dom space [1]. i can say that:

  1. keys are necessary for predictable dom & state updates amongst similar siblings.
  2. keys are typically not an optimization, and - in fact - are usually the opposite.

[1] https://github.com/leeoniya/domvm

@veged

This comment has been minimized.

Show comment
Hide comment
@veged

veged Aug 15, 2017

however, the unambiguous issue here (as @gaearon described) is a fully static usage of arrays and the difference between static arrays and static JSX fragments

veged commented Aug 15, 2017

however, the unambiguous issue here (as @gaearon described) is a fully static usage of arrays and the difference between static arrays and static JSX fragments

@veged

This comment has been minimized.

Show comment
Hide comment
@veged

veged Aug 15, 2017

@brigand O have no doubt that reordering of state-full components may cause some issues ;-) but force every other cases (in my opinion more of them) to struggle for this... looks controversial

veged commented Aug 15, 2017

@brigand O have no doubt that reordering of state-full components may cause some issues ;-) but force every other cases (in my opinion more of them) to struggle for this... looks controversial

@gaearon

This comment has been minimized.

Show comment
Hide comment
@gaearon

gaearon Aug 15, 2017

Member

Keys are important to React. Even if they don't seem to matter in some case (e.g. because components below are all stateless) there is nothing preventing you or someone else on the team to add a stateful component (or even a plain DOM input) somewhere a few levels below in a few months. By then you might forget that you don't have keys, and thus reorders may cause the state (or input value) to be associated with a wrong item.

This is why we encourage you to specify keys everywhere for dynamic lists. Not doing so leads to very hard-to-trace bugs, and we think it's better to spend extra ten seconds specifying the key than ten hours debugging why the state of a component several levels below gets messed up in some corner case.

I fully agree it's inconvenient to specify keys when the list is known to be static and never reorders. You are welcome to discuss this on the JSX repo. If you can't find an issue for this, you are welcome to create a new one there.

Since this thread has a lot of subscribers and the feature has been implemented I would like to lock it to avoid spamming a lot of people with notifications. I hope the clarifications above address your concerns, but if not, you are welcome to create a new issue for further discussion on a specific topic.

Member

gaearon commented Aug 15, 2017

Keys are important to React. Even if they don't seem to matter in some case (e.g. because components below are all stateless) there is nothing preventing you or someone else on the team to add a stateful component (or even a plain DOM input) somewhere a few levels below in a few months. By then you might forget that you don't have keys, and thus reorders may cause the state (or input value) to be associated with a wrong item.

This is why we encourage you to specify keys everywhere for dynamic lists. Not doing so leads to very hard-to-trace bugs, and we think it's better to spend extra ten seconds specifying the key than ten hours debugging why the state of a component several levels below gets messed up in some corner case.

I fully agree it's inconvenient to specify keys when the list is known to be static and never reorders. You are welcome to discuss this on the JSX repo. If you can't find an issue for this, you are welcome to create a new one there.

Since this thread has a lot of subscribers and the feature has been implemented I would like to lock it to avoid spamming a lot of people with notifications. I hope the clarifications above address your concerns, but if not, you are welcome to create a new issue for further discussion on a specific topic.

@facebook facebook locked and limited conversation to collaborators Aug 15, 2017

@sophiebits

This comment has been minimized.

Show comment
Hide comment
@sophiebits

sophiebits Aug 15, 2017

Member

@smrq created a proposal issue for <> syntax on the jsx repo: facebook/jsx#84.

Member

sophiebits commented Aug 15, 2017

@smrq created a proposal issue for <> syntax on the jsx repo: facebook/jsx#84.

@gaearon

This comment has been minimized.

Show comment
Hide comment
@gaearon

gaearon Nov 28, 2017

Member

We just released support for a new React.Fragment export and the associated <> syntax:
https://reactjs.org/blog/2017/11/28/react-v16.2.0-fragment-support.html

Member

gaearon commented Nov 28, 2017

We just released support for a new React.Fragment export and the associated <> syntax:
https://reactjs.org/blog/2017/11/28/react-v16.2.0-fragment-support.html

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.