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

Support (or document non-support) for Fragments #946

Open
alexkrolick opened this Issue Nov 27, 2017 · 26 comments

Comments

Projects
None yet
@alexkrolick

alexkrolick commented Nov 27, 2017

reactjs/reactjs.org#345

React is adding a top-level React.Fragment export that transpiles to JSX as:

<>
  <li>Stuff</li>
  <li>Things</li>
</>

React.createElement(React.Fragment, {}, ...)

Would Preact be able to support fragments or provide some type of fallback?

@leeoniya

This comment has been minimized.

Show comment
Hide comment
@leeoniya

leeoniya commented Nov 27, 2017

@developit

This comment has been minimized.

Show comment
Hide comment
@developit

developit Dec 3, 2017

Owner

+ #929. I might be working on something. In the meantime, it's impossible to really fall back to anything other than inserting a <span> since the diff engine is incapable of dealing with arrays of nodes where there are currently only single nodes supported (components roots).

You can "patch" fragments by setting Babel's pragmaFrag option to "span":

"plugins": [
  ["transform-react-jsx", {
    "pragma": "h",
    "pragmaFrag": "\"span\""
  }]
]
Owner

developit commented Dec 3, 2017

+ #929. I might be working on something. In the meantime, it's impossible to really fall back to anything other than inserting a <span> since the diff engine is incapable of dealing with arrays of nodes where there are currently only single nodes supported (components roots).

You can "patch" fragments by setting Babel's pragmaFrag option to "span":

"plugins": [
  ["transform-react-jsx", {
    "pragma": "h",
    "pragmaFrag": "\"span\""
  }]
]

@developit developit added the duplicate label Dec 3, 2017

@koutsenko

This comment has been minimized.

Show comment
Hide comment
@koutsenko

koutsenko Dec 19, 2017

They introduces < Fragment > component also. In some cases (e.g. old eslint rules) its more preferrable...

koutsenko commented Dec 19, 2017

They introduces < Fragment > component also. In some cases (e.g. old eslint rules) its more preferrable...

@developit

This comment has been minimized.

Show comment
Hide comment
@developit

developit Dec 20, 2017

Owner

If you want to use Fragment faked out for now, just do this:

React.Fragment = 'x-fragment';
Owner

developit commented Dec 20, 2017

If you want to use Fragment faked out for now, just do this:

React.Fragment = 'x-fragment';
@CaptainN

This comment has been minimized.

Show comment
Hide comment
@CaptainN

CaptainN Jan 4, 2018

Where would one put that? React.Fragment = 'x-fragment'; - somewhere in a webpack config?

CaptainN commented Jan 4, 2018

Where would one put that? React.Fragment = 'x-fragment'; - somewhere in a webpack config?

@kurtextrem

This comment has been minimized.

Show comment
Hide comment
@kurtextrem

kurtextrem Jan 6, 2018

@CaptainN I guess in index.js or something similar.

kurtextrem commented Jan 6, 2018

@CaptainN I guess in index.js or something similar.

toshiaki-gohei added a commit to toshiaki-gohei/gohei-mochi that referenced this issue Jan 24, 2018

change to use React instead of preact
preact is good, but
* have not fixed yet? too late...
  developit/preact#915
* want to use JSX Fragments
  developit/preact#946
  @developit
  It's on the ToDo list, but extremely difficult given Preact's current design.
@adamduncan

This comment has been minimized.

Show comment
Hide comment
@adamduncan

adamduncan Feb 5, 2018

Is lack of support for Fragment why we see <undefined> elements in output?

screen shot 2018-02-05 at 19 08 00

Would reassigning React.Fragment to 'x-fragment' be needed in the component that makes use of Fragment, and still result in extraneous custom elements in the DOM? I.e.

import React, { Fragment } from 'react'
React.Fragment = 'x-fragment'

adamduncan commented Feb 5, 2018

Is lack of support for Fragment why we see <undefined> elements in output?

screen shot 2018-02-05 at 19 08 00

Would reassigning React.Fragment to 'x-fragment' be needed in the component that makes use of Fragment, and still result in extraneous custom elements in the DOM? I.e.

import React, { Fragment } from 'react'
React.Fragment = 'x-fragment'
@arusanov

This comment has been minimized.

Show comment
Hide comment
@arusanov

arusanov Feb 15, 2018

@developit any estimate on this feature?

arusanov commented Feb 15, 2018

@developit any estimate on this feature?

@phun-ky

This comment has been minimized.

Show comment
Hide comment
@phun-ky

phun-ky Feb 20, 2018

What is the status of this @developit ? or can this be supported in preact-compat?

phun-ky commented Feb 20, 2018

What is the status of this @developit ? or can this be supported in preact-compat?

@developit

This comment has been minimized.

Show comment
Hide comment
@developit

developit Feb 23, 2018

Owner

Sorry for the radio silence! Right now we just have the hack I posted.

Fragment support requires a complete re-architecture of Preact, which I've been working on. I will be making that work public soon.

Owner

developit commented Feb 23, 2018

Sorry for the radio silence! Right now we just have the hack I posted.

Fragment support requires a complete re-architecture of Preact, which I've been working on. I will be making that work public soon.

@marvinhagemeister

This comment has been minimized.

Show comment
Hide comment
@marvinhagemeister

marvinhagemeister Feb 25, 2018

Collaborator

@developit Any chance to help out with that? I'd love to get involved 🎉

Collaborator

marvinhagemeister commented Feb 25, 2018

@developit Any chance to help out with that? I'd love to get involved 🎉

@developit

This comment has been minimized.

Show comment
Hide comment
@developit

developit Mar 1, 2018

Owner

@marvinhagemeister I just added you as a contributor here. Ping me on slack!

Owner

developit commented Mar 1, 2018

@marvinhagemeister I just added you as a contributor here. Ping me on slack!

@marvinhagemeister

This comment has been minimized.

Show comment
Hide comment
@marvinhagemeister

marvinhagemeister Mar 4, 2018

Collaborator

Just got contributor access and went through a few minor PRs to get started. Implementing Fragments shouldn't be that hard. I did a preview implementation for my preact-server renderer here https://github.com/marvinhagemeister/preact-server-renderer/pull/14/files . Basically a Fragment is a plain component which is treated differently at the render phase.

Before we can tackle it there are a few tasks, which will make adding them much easier:

  1. get dependencies up to date - done
  2. update to babel 7 because of <>foo</> jsx tags. (Fragments aren't much fun without them)
  3. actually implement support for fragments.

I'm currently working on the first to and hopefully we can tackle point 3 soon 🎉

Collaborator

marvinhagemeister commented Mar 4, 2018

Just got contributor access and went through a few minor PRs to get started. Implementing Fragments shouldn't be that hard. I did a preview implementation for my preact-server renderer here https://github.com/marvinhagemeister/preact-server-renderer/pull/14/files . Basically a Fragment is a plain component which is treated differently at the render phase.

Before we can tackle it there are a few tasks, which will make adding them much easier:

  1. get dependencies up to date - done
  2. update to babel 7 because of <>foo</> jsx tags. (Fragments aren't much fun without them)
  3. actually implement support for fragments.

I'm currently working on the first to and hopefully we can tackle point 3 soon 🎉

@phun-ky

This comment has been minimized.

Show comment
Hide comment
@phun-ky

phun-ky Mar 5, 2018

@marvinhagemeister nice work! but I personally feel that we could skip 2. for this fix if possible. I really hope that this fix could be in place before babel@7 enters the playing field, since it's still in beta

phun-ky commented Mar 5, 2018

@marvinhagemeister nice work! but I personally feel that we could skip 2. for this fix if possible. I really hope that this fix could be in place before babel@7 enters the playing field, since it's still in beta

@EmielM

This comment has been minimized.

Show comment
Hide comment
@EmielM

EmielM Mar 5, 2018

I concur with @phun-ky. In my projects we're not using React.Fragment often, but only sporadically where it aids in getting a simpler (flex) layout (ie. preventing extra wrapping). In those few instances, using <React.Fragment> is preferred as it makes more explicit what's going on.

EmielM commented Mar 5, 2018

I concur with @phun-ky. In my projects we're not using React.Fragment often, but only sporadically where it aids in getting a simpler (flex) layout (ie. preventing extra wrapping). In those few instances, using <React.Fragment> is preferred as it makes more explicit what's going on.

@adamduncan

This comment has been minimized.

Show comment
Hide comment
@adamduncan

adamduncan Mar 5, 2018

Nice one @marvinhagemeister. +1 on bypassing point 2. It's not a showstopper after a bit of destructuring.

import React, { Fragment } from 'react'

<Fragment>
  ...
</Fragment>

adamduncan commented Mar 5, 2018

Nice one @marvinhagemeister. +1 on bypassing point 2. It's not a showstopper after a bit of destructuring.

import React, { Fragment } from 'react'

<Fragment>
  ...
</Fragment>
@marvinhagemeister

This comment has been minimized.

Show comment
Hide comment
@marvinhagemeister

marvinhagemeister Mar 5, 2018

Collaborator

I guess I should have phrased the task list differently.

Of course both 1. + 2. are not hard requirements to add support for Fragments. These are just tasks that I'd love to get in before tackling them, that's all.

Collaborator

marvinhagemeister commented Mar 5, 2018

I guess I should have phrased the task list differently.

Of course both 1. + 2. are not hard requirements to add support for Fragments. These are just tasks that I'd love to get in before tackling them, that's all.

@georgiemathews

This comment has been minimized.

Show comment
Hide comment
@georgiemathews

georgiemathews Mar 27, 2018

@marvinhagemeister what’s the progress on this? Is there any way I can help out?

georgiemathews commented Mar 27, 2018

@marvinhagemeister what’s the progress on this? Is there any way I can help out?

@marvinhagemeister

This comment has been minimized.

Show comment
Hide comment
@marvinhagemeister

marvinhagemeister Mar 28, 2018

Collaborator

@georgiemathews We do have Fragments working in a private branch, but we haven't had the time to make it work with hydration yet.

Collaborator

marvinhagemeister commented Mar 28, 2018

@georgiemathews We do have Fragments working in a private branch, but we haven't had the time to make it work with hydration yet.

@prakashsanker

This comment has been minimized.

Show comment
Hide comment
@prakashsanker

prakashsanker Apr 9, 2018

@marvinhagemeister pretty excited for this - when do you think this will merge?

prakashsanker commented Apr 9, 2018

@marvinhagemeister pretty excited for this - when do you think this will merge?

@marvinhagemeister

This comment has been minimized.

Show comment
Hide comment
@marvinhagemeister

marvinhagemeister Apr 30, 2018

Collaborator

@prakashsanker Progress can be tracked here: #1080 🎉

Collaborator

marvinhagemeister commented Apr 30, 2018

@prakashsanker Progress can be tracked here: #1080 🎉

@andrewkesper

This comment has been minimized.

Show comment
Hide comment
@andrewkesper

andrewkesper Aug 9, 2018

I'm able to achieve the desired effect by returning an array of elements.

Here's a simple example of an HTML definition list:

export default class Glossary extends Component {
  state = { 
    glossary: [
      {term: 'HTML', description: 'Hypertext Markup Language'},
      {term: 'JS', description: 'JavaScript'},
      {term: 'CSS', description: 'Cascading Style Sheet'}
    ]
  };
  render({ }, { glossary }) {
    return (
      <dl>
        { glossary.map( g => (
          [ <dt>{g.term}</dt>, <dd>{g.description}</dd> ] // Here is the magic
        )) }
      </dl>
    );
  }
}

HTML output:

<dl>

  <dt>HTML</dt>
  <dd>Hypertext Markup Language</dd>

  <dt>JS</dt>
  <dd>JavaScript</dd>

  <dt>CSS</dt>
  <dd>Cascading Style Sheet</dd>

</dl>

Working demo here

andrewkesper commented Aug 9, 2018

I'm able to achieve the desired effect by returning an array of elements.

Here's a simple example of an HTML definition list:

export default class Glossary extends Component {
  state = { 
    glossary: [
      {term: 'HTML', description: 'Hypertext Markup Language'},
      {term: 'JS', description: 'JavaScript'},
      {term: 'CSS', description: 'Cascading Style Sheet'}
    ]
  };
  render({ }, { glossary }) {
    return (
      <dl>
        { glossary.map( g => (
          [ <dt>{g.term}</dt>, <dd>{g.description}</dd> ] // Here is the magic
        )) }
      </dl>
    );
  }
}

HTML output:

<dl>

  <dt>HTML</dt>
  <dd>Hypertext Markup Language</dd>

  <dt>JS</dt>
  <dd>JavaScript</dd>

  <dt>CSS</dt>
  <dd>Cascading Style Sheet</dd>

</dl>

Working demo here

@apfelbox

This comment has been minimized.

Show comment
Hide comment
@apfelbox

apfelbox Aug 9, 2018

@andrewkesper that works if you are inside a JSX.Element. What fragments explicitly allow is something else: returning "an array" directly from render(), so that you don't need to have a single root element on components (at least non that ends up in the DOM).

apfelbox commented Aug 9, 2018

@andrewkesper that works if you are inside a JSX.Element. What fragments explicitly allow is something else: returning "an array" directly from render(), so that you don't need to have a single root element on components (at least non that ends up in the DOM).

@qodesmith

This comment has been minimized.

Show comment
Hide comment
@qodesmith

qodesmith Aug 24, 2018

In React it seems we can make our own Fragment component like this:

import React from 'react'
const Fragment = ({ children }) => React.Children.toArray(children)
export default Fragment

Could the same thing be done with Preact?

qodesmith commented Aug 24, 2018

In React it seems we can make our own Fragment component like this:

import React from 'react'
const Fragment = ({ children }) => React.Children.toArray(children)
export default Fragment

Could the same thing be done with Preact?

@marvinhagemeister

This comment has been minimized.

Show comment
Hide comment
@marvinhagemeister

marvinhagemeister Aug 24, 2018

Collaborator

No, current preact expects a DOM node per vnode. This is not true for Fragments where the whole point of them is to not render anything into the DOM. So this is not easily doable (if at all) with the current architecture.

That said we have merged the PR for Fragments into our development repo last week. Fragments will be part of the next major release🎉

It is not available to the public yet and we don't have a release estimate for now. It's progressing nicely and we are currently working through an ever shrinking number of failing test cases and ensuring that all known bugs in the next version are fixed.

Collaborator

marvinhagemeister commented Aug 24, 2018

No, current preact expects a DOM node per vnode. This is not true for Fragments where the whole point of them is to not render anything into the DOM. So this is not easily doable (if at all) with the current architecture.

That said we have merged the PR for Fragments into our development repo last week. Fragments will be part of the next major release🎉

It is not available to the public yet and we don't have a release estimate for now. It's progressing nicely and we are currently working through an ever shrinking number of failing test cases and ensuring that all known bugs in the next version are fixed.

@dmbdesignpdx

This comment has been minimized.

Show comment
Hide comment
@dmbdesignpdx

dmbdesignpdx Sep 15, 2018

Hi, I know this is late, but I just wanted to share a possible temporary solution until Fragments are included. At least this has worked for me...

function ListItems() {
  return ([
    <li></li>,
    <li></li>
  ])
}

class List extends Component {
  ...
  render() {
    return (
      ...
      <ListItems />
      ...
    )
  }
}

The <ListItems />, of course, will result in becoming <undefined></undefined>.
BUT, if you change it to a function call within a JSX expression:

class List extends Component {
  ...
  render() {
    return (
      ...
      { ListItems() }
      ...
    )
  }
}

It will work as expected.

Hope that helps someone!

dmbdesignpdx commented Sep 15, 2018

Hi, I know this is late, but I just wanted to share a possible temporary solution until Fragments are included. At least this has worked for me...

function ListItems() {
  return ([
    <li></li>,
    <li></li>
  ])
}

class List extends Component {
  ...
  render() {
    return (
      ...
      <ListItems />
      ...
    )
  }
}

The <ListItems />, of course, will result in becoming <undefined></undefined>.
BUT, if you change it to a function call within a JSX expression:

class List extends Component {
  ...
  render() {
    return (
      ...
      { ListItems() }
      ...
    )
  }
}

It will work as expected.

Hope that helps someone!

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