如何在React 16中使用ReactDOM.createPortal()?

时间:2017-09-24 18:48:56

标签: javascript reactjs ecmascript-6

我正在使用React 16并需要门户网站,但无法找到有关此功能的任何文档。有谁知道如何使用它?

https://github.com/facebook/react/pull/10675

感谢您的任何建议。

4 个答案:

答案 0 :(得分:33)

React v16刚刚在几个小时前发布(Yay !!),正式支持Portal

什么是Portal?它已经有多长时间了?

  

Portals提供了一种将子项呈现为存在于父组件的DOM层次结构之外的DOM节点的第一类方法。

Portal在反应社区中不是新概念。许多库都支持这种功能。例如react-portalreact-gateway

渲染任何反应应用时会发生什么?

通常,在渲染任何React应用程序时,使用单个DOM元素来渲染整个React树。

class HelloReact extends React.Component {
   render() {
       return (
          <h1>Hello React</h1>
       );
   }
}

ReactDOM.render(<HelloReact />, document.getElementById('root'));

如您所见,我们将react组件呈现为ID为root的DOM元素。

什么是Portal,为什么需要它?它为什么存在?

门户是一种在父组件 的主DOM层次结构之外呈现React子元素而不会丢失响应上下文 的方法。我强调它,因为非常受欢迎的库,如react-routerredux大量使用反应上下文。因此,使用Portal时的上下文可用性非常有用。

根据react文档,

  

门户网站的一个典型用例是当父组件具有溢出:隐藏或z-index样式时,但是您需要在视觉上对孩子进行突破&#34;它的容器。例如,对话框,hovercards和工具提示。

因此,使用门户,您可以在需要时将并行响应树呈现到另一个DOM节点上。即使它在不同的DOM节点中呈现,父组件也可以捕获未捕获的事件。请参阅文档中提供的此codepen

以下示例应该提供更多想法:

// index.html
<html>
    <body>
        <div id="root"></div>
        <div id="another-root"></div>
    </body>
</html>

// index.jsx
const mainContainer = document.getElementById('root');
const portalContainer = document.getElementById('another-root');

class HelloFromPortal extends React.Component {
    render() {
         return (
           <h1>I am rendered through a Portal.</h1>
         );
    }
}

class HelloReact extends React.Component {
    render() {
        return (
             <div>
                 <h1>Hello World</h1>
                 { ReactDOM.createPortal(<HelloFromPortal />, portalContainer) }
             </div>
        );
    }
}

ReactDOM.render(<HelloReact />, mainContainer);

https://codesandbox.io/s/62rvxkonnw

您可以使用devtools inspect元素并查看<h1>I am rendered through a Portal.</h1>标记内的#another-root,而<h1>Hello World</h1>标记内显示#root

希望这会有所帮助:)

更新:回答@PhillipMunin's comment

  

ReactDOM.renderReactDOM.createPortal之间有什么不同?   ReactDOM.createPortal

  1. 即使通过门户呈现的组件呈现在其他位置(在当前容器根目录之外),它仍然作为同一父组件的子项存在。 (谁调用了ReactDOM.render)因此,孩子的任何事件都会传播给父母。 (Ofc,如果您手动停止事件的传播,这不起作用。)

  2. 通过门户网站呈现的组件内部可以访问相同的上下文。但不是在我们直接{{1}}的情况下。

  3. 我已经创建了另一个演示来说明我的观点。 https://codesandbox.io/s/42x771ykwx

答案 1 :(得分:3)

我认为我们可以通过在'root'元素外部创建一个外部dom节点来实现相同的功能

node = document.createElement('div')
document.body.appendChild(node)
ReactDOM.render(<Modal {...props} />,node)

假设我们正在创建一个模式视图,我们可以为其创建包装器

import React, { useEffect } from 'react'
import ReactDOM from 'react-dom'
import Modal from './Modal'
let node = null
const ModalWrapper = (props) =>{
    useEffect(()=>{
        node && ReactDOM.render(<Modal {...props} />,node)
    })
    useEffect(()=>{
        node = document.createElement('div')
        document.body.appendChild(node)
        ReactDOM.render(<Modal {...props} />,node)
    },[])
    return(
        <script />
    )
}

下面是一个示例Create Modal with React Portal and React Hooks from scratch without external library

React-modal with portal

答案 2 :(得分:0)

通过在组件的createPortal方法中调用render方法来创建门户网站。

render() {
  return (
    <div>
      {ReactDOM.createPortal(this.renderPortal(), this.props.portalEl)}
    </div>
  )
}

renderPortal应返回要在门户网站内呈现的内容,而portalEl是将接收内容的外部DOM元素。

最近有人提到门户网站的信息可以在React tests找到。

答案 3 :(得分:0)

对于任何想知道的人,这就是我如何在Typescript中实现自己的自定义Portal组件

    import React from 'react'
    import ReactDOM from 'react-dom'

    const Portal: React.FC<PortalProps> = (props) => {
        return ReactDOM.createPortal(props.children, props.target)
    }

    export interface PortalProps {
        target: Element;
        children: React.ReactNode;
    }

    export default Portal