美文网首页
React.createPortal 及 Modal 的新实现方

React.createPortal 及 Modal 的新实现方

作者: Small_Song | 来源:发表于2022-04-28 15:47 被阅读0次

    一、描述

    一般使用 React 的组件都是挂到父组件的 this.props.children 上面,总是被最近的父组件所捕获,最终到 React 根组件上。

    而 Protals 则提供了一种将组件直接挂载到直接父组件 DOM 层次之外的一类方式。

    react-dom 提供的具体方法是 ReactDOM.createPortals(child, container),这个方法需要两个参数,第一个参数是需要挂载的组件实例,而第二个参数则是要挂载到的DOM节点。一般来说第一个参数可能传递的是需要挂载的 this.props.children

    二、之前的 Modal 组件

    以一个 Modal 弹窗举例(官网也是用的这个例子)。

    正常来说,我要创建一个 Modal 组件,可以按照下面这么写:

    
    const styles = {
      modal: {
        position: 'fixed',
        top: 0,
        left: 0,
        right: 0,
        bottom: 0,
        backgroundColor: 'rgba(0,0,0,0.3)',
        display: 'flex',
        justifyContent: 'center',
        alignItems: 'center'
      }
    }
     
    class Modal extends Component {
      constructor(props) {
        super(props);
      }
      render() {
        return (
          <div style={styles.modal}>
            {this.props.children}
          </div>
        );
      }
    }
    

    而使用的时候直接引入并且写子组件即可:

    <div className="App">
            <Modal>
              <h1>Modal</h1>
            </Modal>
     </div>
    

    最终的效果如下:


    Modal 的组件实例这种实现方式也可以,这里的突破方式其实就是直接将 Modal 组件位置进行 fixed 定位。

    但是更多时间如果父组件容器存在 overflow: hidden 或者存在 z-index 的时候,需要在视觉上突破父组件的容器,使用 createPortal 更好一些,因为他可以随便将组件挂到任何的 DOM 下。

    三、 createProtal 改造 Modal 组件:

    1、html 预留容器

    在 html 中除了 div#root 之外,给 Modal 预留了一个新的 div#modal-root,:

    const appRoot = document.getElementById('root');
    const modalRoot = document.getElementById('modal-root');
    

    为了突出 div#root 容器宽度高度很小,并且 overflow:hidden,给 div#root 一个样式:

    #root {
      height: 10em;
      width: 10em;
      background: lightblue;
      overflow: hidden;
    }
    

    2、改造 Modal 容器

    新的 Modal 容器组件内容如下:

    
    class ModalContainer extends 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
        );
      }
    }
    

    因为需要保证 ModalContainer 始终保持在最上层,不会被其他 z-index 覆盖掉,因此需要给 div#modal-root 一个样式:

    #modal-root {
      position: relative;
      z-index: 999;
    }
    

    3、Modal 组件的内容

    上面只是 Modal 的容器组件,最主要的作用就是将 this.props.children 挂载到组件外部去(div#modal-root

    需要创建 Modal 需要显示的内容组件:

    #modal-root {
      position: relative;
      z-index: 999;
    }
    

    4、使用 Modal 容器组件和内容组件

    将 ModalContent 挂载到 ModalContainer 中即可:

    
    class App2 extends Component {
      state = {
        name: 'clickme'
      }
      componentDidMount(){
        // console.log(findDOMNode(ref.current))
      }
      clickHandle = () => {
        this.setState({
          name: 'clickme' + Date.now()
        });
      }
    render() {
      return ( 
          <div className="App">
            <ModalContainer>
              <ModalContent />
            </ModalContainer>
          </div>
      );
    }
    }
    

    5、效果

    可以发现,即使主组件是 overflow:hidden 的,但是 modal 能够正常出现,同时,因为 div#modal-rootz-index: 999,因此会置于最上层。

    可以发现,在父组件里捕获一个来自 Portal 的事件冒泡很简单,在开发时不需要完全依赖于 Portal ,对于事件的捕获更加的灵活。

    6、事件

    import React, { Component } from "react";
    import ReactDOM from "react-dom";
    
    export default class Portals extends Component {
      render() {
        return (
          <div
            onClick={() => {
              console.log("rooter click");
            }}
          >
            <p>我想出现在root中</p>
            <Test />
          </div>
        );
      }
    }
    
    function Test() {
      return ReactDOM.createPortal(
        <ChildA />,
        //     <h1>我想出现在container中</h1>,
        document.getElementById("container")
      );
    }
    
    function ChildA() {
      return <p>我是childA</p>;
    }
    

    注意

    1.React中的事件是包装过的

    2.它的事件冒泡是根据虚拟DOM树来冒泡,与真实的DOM树无关

    相关文章

      网友评论

          本文标题:React.createPortal 及 Modal 的新实现方

          本文链接:https://www.haomeiwen.com/subject/ljddyrtx.html