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, ); }}