React: ReactDOM.createPortal support Fragment?

Created on 29 Nov 2017  路  7Comments  路  Source: facebook/react

Hello. Maybe it's possible add support Fragment for .createPortal method?
Example this code add wrapper for buttons

class FormButtons extends PureComponent {
  constructor(props) {
    super(props);
    this.el = document.createElement('span');
    // this.el = <Fragment />;  // - it's the best solution without any wrappers
  }

  componentDidMount() {
    const { portalSelector } = this.props;
    document.getElementById(portalSelector).appendChild(this.el);
  }

  componentWillUnmount() {
    const { portalSelector } = this.props;
    document.getElementById(portalSelector).removeChild(this.el);
  }

  render() {
    const { submit, reset, submitting, pristine } = this.props;
    return ReactDOM.createPortal(
      <Fragment>
        <button
          className="btn btn-primary btn-block"
          type="submit"
          onClick={submit}
          disabled={submitting}
        >
          袟邪泻邪蟹邪褌褜
        </button>
        <button
          className="btn btn-primary btn-block"
          type="button"
          onClick={reset}
          disabled={submitting || pristine}
        >
          小斜褉芯褋懈褌褜
        </button>
      </Fragment>,
      this.el
    );
  }
}

Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?
16.2

Needs More Information

Most helpful comment

In React, there is a distinction between React elements and DOM elements.

Typically, your app is composed of React elements. However, they need to render into some DOM element. Typically you render your React <App /> into some DOM container, e.g. document.getElementById('root').

Portals let you render a React element subtree into a different DOM element. For example, you can render <Modal> into document.getElementById('modal-container').

Your question is confusing me because the target can't be a React element. The point of portals is to render some React tree into a different DOM element. <Fragment> is not a DOM element. It is a React element. You can't render "into" it, and thus it can't be a target of the portal.

However I would expect it to be possible to render a <Fragment> into some DOM element, with or without a portal. I hope this makes sense!

All 7 comments

I don't understand this report. Is this a bug report, a feature request, or both combined?

Is using <Fragment> inside ReactDOM.createPortal not working for you? Can you create an isolated example?

Or is this a feature request about not having to create a target node? Then it's not clear how React could guess where to insert the items.

@gaearon This example https://codesandbox.io/s/j3qry8r38y
I see what createPortal receive as second argument the domNode. Why this domNode cannot be new Fragment?

PS Close this. I understand what it's impossible.

In React, there is a distinction between React elements and DOM elements.

Typically, your app is composed of React elements. However, they need to render into some DOM element. Typically you render your React <App /> into some DOM container, e.g. document.getElementById('root').

Portals let you render a React element subtree into a different DOM element. For example, you can render <Modal> into document.getElementById('modal-container').

Your question is confusing me because the target can't be a React element. The point of portals is to render some React tree into a different DOM element. <Fragment> is not a DOM element. It is a React element. You can't render "into" it, and thus it can't be a target of the portal.

However I would expect it to be possible to render a <Fragment> into some DOM element, with or without a portal. I hope this makes sense!

@gaearon Hello. I solved this problem. I use as this.el = document.createDocumentFragment() to .createPortal method with Fragment as parentNode. Your last sentence made me think about this thought

Nice! That should work yeah. But keep in mind React <Fragment> is not the same as createDocumentFragment() (in fact React <Fragment> doesn't use DOM at all, which is why it works in React Native etc).

@romanlex I just want to comment that when using this.el = document.createDocumentFragment() then document.body.removeChild(this.el) would fail because this.el was never attached to the body but only the child components.

Also wanted to mention that with DOM fragments I've run into the exception:

NotFoundError: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.

That was happening when a portal was unmounting; No issues when using DOM elements: this.el = document.createElement(...).

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jimfb picture jimfb  路  3Comments

UnbearableBear picture UnbearableBear  路  3Comments

framerate picture framerate  路  3Comments

trusktr picture trusktr  路  3Comments

zpao picture zpao  路  3Comments