How to create a React Modal(which is append to `<body>`) with transitions? How to create a React Modal(which is append to `<body>`) with transitions? reactjs reactjs

How to create a React Modal(which is append to `<body>`) with transitions?


At react conf 2015, Ryan Florence demonstrated using portals. Here's how you can create a simple Portal component...

var Portal = React.createClass({  render: () => null,  portalElement: null,  componentDidMount() {    var p = this.props.portalId && document.getElementById(this.props.portalId);    if (!p) {      var p = document.createElement('div');      p.id = this.props.portalId;      document.body.appendChild(p);    }    this.portalElement = p;    this.componentDidUpdate();  },  componentWillUnmount() {    document.body.removeChild(this.portalElement);  },  componentDidUpdate() {    React.render(<div {...this.props}>{this.props.children}</div>, this.portalElement);  }});

and then everything you can normally do in React you can do inside of the portal...

    <Portal className="DialogGroup">       <ReactCSSTransitionGroup transitionName="Dialog-anim">         { activeDialog === 1 &&             <div key="0" className="Dialog">              This is an animated dialog            </div> }       </ReactCSSTransitionGroup>    </Portal> 

jsbin demo

You can also have a look at Ryan's react-modal, although I haven't actually used it so I don't know how well it works with animation.


I wrote the module react-portal that should help you.

Usage:

import { Portal } from 'react-portal'; <Portal>  This text is portaled at the end of document.body!</Portal> <Portal node={document && document.getElementById('san-francisco')}>  This text is portaled into San Francisco!</Portal>


React 15.x

Here's an ES6 version of the method described in this article:

import React from 'react';import ReactDOM from 'react-dom';import PropTypes from 'prop-types';export default class BodyEnd extends React.PureComponent {        static propTypes = {        children: PropTypes.node,    };        componentDidMount() {        this._popup = document.createElement('div');        document.body.appendChild(this._popup);        this._render();    }    componentDidUpdate() {        this._render();    }    componentWillUnmount() {        ReactDOM.unmountComponentAtNode(this._popup);        document.body.removeChild(this._popup);    }    _render() {        ReactDOM.render(this.props.children, this._popup);    }        render() {        return null;    }}

Just wrap any elements you want to be at the end of the DOM with it:

<BodyEnd><Tooltip pos={{x,y}}>{content}</Tooltip></BodyEnd>

React 16.x

Here's an updated version for React 16:

import React from 'react';import ReactDOM from 'react-dom';export default class BodyEnd extends React.Component {    constructor(props) {        super(props);        this.el = document.createElement('div');        this.el.style.display = 'contents';        // The <div> is a necessary container for our        // content, but it should not affect our layout.        // Only works in some browsers, but generally        // doesn't matter since this is at        // the end anyway. Feel free to delete this line.    }        componentDidMount() {        document.body.appendChild(this.el);    }    componentWillUnmount() {        document.body.removeChild(this.el);    }        render() {        return ReactDOM.createPortal(            this.props.children,            this.el,        );    }}

Working example