React Portal解决了什么?

时间:2018-12-09 12:22:57

标签: javascript reactjs modal-dialog

来自react portal的文档:

  

门户网站的典型用例是父组件出现溢出:隐藏或z-index样式,但是您需要子组件在视觉上“突破”其容器。例如,对话框,悬浮卡和工具提示。

建议的解决方案是使用:

// this line is rendered in `Portal1` component,
// which is rendered in `Parent1` component.
ReactDOM.createPortal(Child1, Container1)

我不知道它能解决什么。为什么让Child1的{​​{1}}代替Container1成为孩子?

我的问题可能不清楚,如果不是->此解决方案与其他用于创建“对话框,悬浮卡和工具提示”的解决方案有何不同?

3 个答案:

答案 0 :(得分:3)

在React V15中,我们只能将子级dom添加到父级dom中,这意味着,如果要具有一个元素,则必须创建一个新的div。像这样:

<div>
    {this.props.children}
</div>

在React v16中,不需要创建新的div。我们可以使用门户网站将children元素添加到dom树中的任何dom中。

ReactDOM.createPortal(
    this.props.children,
    domNode
  );

溢出:隐藏或z-index样式

如果父级组件有溢出:隐藏或z-index样式,而子级元素类型是对话框,悬浮卡,工具提示等,则这些元素应位于父级元素的上层,表示中断。它们可能被父组件遮住了。

因此createPortal提供了一个更好的选择。它可以加载到父组件的上部组件上。将元素安装到另一个dom后,将不再受到保护。


事件和泡沫

即使该组件已安装在另一个组件上,事件也可能上升到父组件。

答案 1 :(得分:2)

初始化React应用程序时,ReactDOM会问一个DOM容器,您所有的React组件都将在此DOM下呈现。这使得React可以处理所有渲染过程。

但是有时您需要控制一些React组件以在不同的DOM元素下呈现并与您现有的React应用程序进行交互。在这种情况下,React Portal十分方便。

当React在底层创建虚拟元素时,您不能将它们转换为DOM元素并将其直接插入DOM中。 React Portals允许您传递React Elements并为其指定容器DOM。

以下是演示示例

您拥有Modal组件,该组件将div元素呈现在中心。

function Modal() {
  return (
      <div style={{ position: 'absolute', left: '50%'}}>
          Message
      </div>
  );
}

一个将您的Modal组件放入div内,该组件具有相对位置。

<div style={{ position: 'relative', left: 100 }}>
  <Modal />
</div>

问题在于渲染Modal组件时,其位置相对于父级div的位置,但是您需要在窗口中心显示它。

为了解决此问题,您可以直接将Modal组件附加到body元素中。使用门户网站可以轻松完成此操作。

这是门户网站的解决方案。

function ModalRenderer() {
  return (
      React.createPortal(
         <Modal />,
         document.body
      )
  );
}

并在应用程序内的任何地方使用ModalRenderer组件。

<div style={{ position: 'relative', left: 100 }}>
  <ModalRenderer />
</div>

这样,您的ModalRenderer决定了Modal的容器元素,该元素与您的应用程序树无关。

答案 2 :(得分:1)

一个很好的案例是分离CSS问题。

这里是一个例子:

HTML

A

CSS

<div id="app-root"></div>
<div id="modal-root"></div>

通天塔

.app {
  position: fixed;
  height: 100%;
  width: 100%;
  top: 0;
  left: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
}

.modal {
  background-color: rgba(0,0,0,0.5);
}

观察: 1.正文的位置为const appRoot = document.getElementById('app-root') const modalRoot = document.getElementById('modal-root') class App extends React.Component { constructor(props) { super(props); this.state = { showModal: false } this.handleShow = this.handleShow.bind(this); this.handleHide = this.handleHide.bind(this); } handleShow() { this.setState({ showModal: true }) } handleHide() { this.setState({ showModal: false }) } render() { const modal = this.state.showModal ? ( <Modal> <div className="modal">I am no longer centered!</div> </Modal> ) : null return ( <div className='app'> Basic <button onClick={this.handleShow}> Show Modal </button> {modal} </div> ) } } class Modal extends React.Component { constructor(props) { super(props); this.el = document.createElement('div'); } componentDidMount(){ modalRoot.appendChild(this.el) } componentWillUnmount() { modalRoot.removeChild(this.el) } render() { return ReactDOM.createPortal( this.props.children, this.el ) } } ReactDOM.render(<App />, appRoot) ,居中 2.当我们点击按钮时,文本弹出。请注意,文本不再居中! 3.如果我们已经将它用于类似这样的事情:

fixed

注意文本居中。模态的工作方式是转到 <div className='app'> Basic <button onClick={this.handleShow}> Show Modal </button> <div className="modal">I am centered ;(</div> </div> 并将该DOM元素附加到那里。您可以在modal-root下拥有自己的CSS,并与父组件(modal-root)分开。

您现在不再有义务将“孩子”组件附加到父组件下。在这种情况下,您(app-root)会将其附加到同级组件(​​{{1} }。您可以将其完全附加到app-root或任何您想要的元素上。像其他用户提到的那样,另一个特权是,事件冒泡的发生就像该子组件是他们自己的子组件一样。

  

#app-root中的Parent组件将能够捕获来自同级节点#modal-root的未捕获的冒泡事件。   Source