react Portals(插槽)

这篇文章说说关于Protals2个点:

  • Portals的概念及使用
  • Protals的事件冒泡

一、关于reactPortal插槽。根据官网的说法:Portals传送门

Portal提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优秀的方案。
一个 portal 的典型用例是当父组件有 overflow: hidden 或 z-index 样式时,但你需要子组件能够在视觉上“跳出”其容器。例如,对话框、悬浮卡以及提示框。就是这种需要在某个组件中使用,但是视图上需要在全局的范围。

先来看下Portal的用法:第一个参数(child)是任何可渲染的 React 子元素,例如一个元素,字符串或 fragment。第二个参数(container)是一个 DOM 元素。

ReactDOM.createPortal(child, container) 
// child即子组件中的内容,如`<Modal><div>test</div><</Modal>`组件中的`<div>test</div>`

由于是在我现有测试项目的基础上改造,所以和官网例子稍微有点不同。不过是可以对照着看的:
App.js

//  这是app根组件,Routers渲染home页。也就是说,我们即将要渲染的弹窗会在id为`box`的`div`中。和渲染出的home平级。
import Routers from './router/router';

class App extends Component {
  render () {
    return (
      <div className="test">
          <div id="box"></div>
          <Routers></Routers>  
      </div>
      
    )
  }
}

export default App;

home.js


/* eslint-disable */
import React from "react";
import Header from '../../component/header/header.js';
import Footer from '../../component/footer/footer';
import Sidebar from '../../component/sidebar/sidebar';
import './home.scss'

const modalRoot = document.getElementById('box');   // 获取`id`为`box`的`dom`

// 弹窗组件
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,
      );
    }
  }


// 父组件
class Home extends Component {
    constructor(props) {
        super(props)
        this.state = {
            showModal: false,
        }
    }
    
    handleShow = () => {
        this.setState({
            showModal: true,
        })
    }
    handleHide = () => {
        this.setState({
            showModal: false,
        })
    }

    getModal = () => {
        console.log('这是Modal冒泡的事件')
    }

    render () {        
        const modal = this.state.showModal ? (
                      
            <Modal>
                <div className="modal">
                    <div>
                        With a portal, we can render content into a different
                        part of the DOM, as if it were any other React child.
                    </div>
                    This is being rendered inside the #modal-container div.
                    <button onClick={this.handleHide}>Hide modal</button>
                </div>
            </Modal>

        ) : null;


        return (
            <div className="test_home" id="test-home" >
                <div className="app" onClick={ this.getModal.bind(this) }>
                    This div has overflow: hidden.
                    <button onClick={ this.handleShow.bind(this) }>Show modal</button>
                    {modal}
                </div>
            </div>
        ) 
    }
}

export default Home 

home.scss

.app {
    height: 10em;
    width: 10em;
    background: lightblue;
    overflow: hidden;
}
  
.modal {
    background-color: rgba(0,0,0,0.5);
    position: fixed;
    height: 100%;
    width: 100%;
    top: 0;
    left: 0;
    display: flex;
    align-items: center;
    justify-content: center;
}

代码逻辑: 在App根组件,放置了一个id为boxdom,同级放置了一个渲染home页面的Router组件。在home组件中渲染modal
此时,modalhome组件的子节点。当showModalfalse的时候,不进行渲染。为true时,渲染Modal组件。Modal组件先是创建了一个div并且插入到id为boxdiv中。利用ReactDOM.createPortalModal中的节点渲染到创建的div中。

渲染结果
在这里插入图片描述
下面是注释掉modalRoot.removeChild(this.el);
在这里插入图片描述

这里Modal组件最终渲染在父组件Home之外的App组件id为box的dom上。
建议:项目中实际使用时,可以将Modal单独写一个文件。直接引用就可以。最大限度的复用可以降低冗余。
代码中Modal组件,在componentDidMountcomponentWillUnmount中分别添加和移除了dom。可以试一下,如果不移除,那么每渲染一次Modal节点中就会多出一对div节点。这样不符合我们优化的思想。

二、Protals事件冒泡。上面的代码在Home组件下classNameapp的dom上添加了getModal事件。这时候我们运行程序,点击Hide modal按钮,打开控制台,会发现打印出这是Modal冒泡的事件。虽然Modal最终是挂载到box下面。但从Modal中冒泡上来的事件依然能被{modal}的所有parents捕捉到。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值