react模拟弹窗组件

1,871 阅读2分钟

1.主要API

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

参考:Portals

// 第一个参数(child)是任何可渲染的 React 子元素,例如一个元素,字符串或 fragment。
// 第二个参数(container)是一个 DOM 元素。
React.createPortal(child,container)

2.具体实现

index.js

import React, { Component } from "react";
import { createPortal } from "react-dom";
import "./index.css";

export default class Modal extends Component {
  // 监听父组件visible变化,控制modal组件的显示和隐藏
  static getDerivedStateFromProps(nextProps, prevState) {
    if (nextProps.visible !== prevState.visible) {
      return {
        visible: nextProps.visible,
      };
    }
    return null;
  }

  constructor(props) {
    super(props);
    // 创建modal组件挂载的节点
    this.node = document.createElement("div");
    document.body.appendChild(this.node);
    this.state = {
      visible: props.visible,
    };
  }

  // 组件卸载前,移除挂载的节点
  componentWillUnmount() {
    document.body.removeChild(this.node);
  }

  onCancel = () => {
    const { onCancel } = this.props;
    onCancel && onCancel();
  };

  onOk = () => {
    const { onOk } = this.props;
    onOk && onOk();
  };

  render() {
    const { title } = this.props;
    const { visible } = this.state;
    // 将modal组件挂载“跳出”当前dom节点
    return createPortal(
      visible && (
        <div>
          <div className="modal-mask"></div>
          <div className="modal-wrap">
            <div className="modal">
              <div className="modal-close" onClick={this.onCancel}>
                X
              </div>
              <div className="modal-header">{title}</div>
              <div className="modal-body">{this.props.children}</div>
              <div className="modal-footer">
                <button onClick={this.onCancel}>取消</button>
                <button onClick={this.onOk} className="btn-primary">
                  确定
                </button>
              </div>
            </div>
          </div>
        </div>
      ),
      this.node
    );
  }
}

index.css // 样式参考antd

.modal-mask{
  position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    z-index: 1000;
    height: 100%;
    background-color: rgba(0,0,0,.45);
}
.modal-wrap{
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 1000;
  overflow: auto;
  outline: 0;
  -webkit-overflow-scrolling: touch;
}
.modal{
    box-sizing: border-box;
    color: rgba(0,0,0,.65);
    font-size: 14px;
    line-height: 1.5715;
    position: relative;
    top: 100px;
    width: 520px;
    margin: 0 auto;
    background-color: #fff;
}
.modal-header{
  padding: 16px 24px;
  border-bottom: 1px solid #f0f0f0;
  text-align: center;
  border-radius: 2px 2px 0 0;
}
.modal-close{
  position: absolute;
  top: 0;
  right: 0;
  padding: 16px 24px;
  cursor: pointer;
}
.modal-body{
  padding: 24px;
  text-align: left;
}
.modal-footer{
  padding: 16px 24px;
  border-top: 1px solid #f0f0f0;
  text-align: right;
}
.modal-footer button+button{
  margin-left: 8px;
}
.modal-footer button{
  position: relative;
  font-weight: 400;
  white-space: nowrap;
  text-align: center;
  background-image: none;
  border: 1px solid #d9d9d9;
  height: 32px;
  padding: 4px 15px;
  border-radius: 2px;
  background-color: #fff;
  cursor: pointer;

}
button.btn-primary{
  background-color: #1890ff;
  border-color: #1890ff;
  color: #fff;
}