Portal 提供了一种将子节点渲染到存在于父节点以外的DOM节点的优秀方案;

尽管 portal 可以被放置在 DOM 树中的任何地方,但在任何其他方面,其行为和普通的 React 子节点行为一致。由于 portal 仍存在于 React 树, 且与 DOM 树 中的位置无关,那么无论其子节点是否是 portal,像 context 这样的功能特性都是不变的。

这包含事件冒泡。一个从 portal 内部触发的事件会一直冒泡至包含 React 树*的祖先,即便这些元素并不是 *DOM 树 中的祖先

通俗来讲就是假设父节点app-root 通过 ReactDOM.createPortal 将它的子节点,挂载到它的兄弟节点modal-root上,父节点app-root的Partal组件仍然能狗捕获到该子节点冒泡上来的事件。

下面来实现一个建议的弹窗功能

创建Portal插槽组件MsgConten

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

class Model extends React.Component{

    constructor(props) {
        super(props);
        this.el = document.createElement('div');
        this.modelRoot = document.getElementById('model-root');
    }

    componentDidMount(){
     //将插入弹窗的div放入到兄弟节点“model-root”中
this.modelRoot.appendChild(this.el); } //清理工作 componentWillUnmount() { this.modelRoot.removeChild(this.el); } render() { return ReactDOM.createPortal((//将创建好的弹窗插入到创建的div元素中 <MsgWindow closeModel={this.props.closeModel}/> ), this.el); } } //弹窗 function MsgWindow(props) { return( <div className={'cover'} style={{background:'grey',width:'200px',height:'200px',margin: '0 auto'}}> <button onClick={props.closeModel}>关闭</button> <div style={{color:'#ffffff'}}> 我是app-root的子节点,但被渲染在它的兄弟节点model-root上 </div> </div> ) } export default Model;

创建两个兄弟节点app-root和model-root

import React,{Component} from 'react';
import Model from './MsgConten';

class Portals extends Component{

    state={
        showMsg:false
    };

    handleMsgWindow(){
        this.setState({
            showMsg:!this.state.showMsg
        })
    }

    render() {
        return (
            <>
                <div id={"app-root"} >
                    {(this.state.showMsg == true)?<Model isShow={this.state.showMsg} closeModel={this.handleMsgWindow.bind(this)}/>:null}
                    <button onClick={this.handleMsgWindow.bind(this)}>portals</button>
                </div>
                <div id={"model-root"}></div>
            </>
        )
    }
}

export default Portals;

运行效果如下

12-29 19:36
查看更多