如何用 react封装一款Modal 模态对话框

2,013 阅读3分钟

这是我参与11月更文挑战的第3天,活动详情查看:2021最后一次更文挑战

TIP 👉 不畏浮云遮望眼,只缘身在最高层。——王安石《登飞来峰》

前言

Web Component是前端界一直非常热衷的一个领域,用第三方组件化的框架去实现的话,你需要依赖框架本身很多东西,很多时候我们只是简单的几个组件,不是很大,也不是很多,所以为了保证组件的`轻量,简单`,其实这个时候我们并不想采用第三方的框架。

Modal 模态对话框

import


import Modal from '@/components/Modal/Modal';

Props

1. visible

  • 类型:bool

  • 默认值:false

  • 说明:是否可见, true表示可见

2. type

  • 类型:string

  • 默认值:无

  • 说明:类型

3. width

  • 类型:string|number

  • 默认值:520

  • 说明:宽度

4. title

  • 类型:string|ReactNode

  • 默认值:无

  • 说明:标题

5. children

  • 类型:string|ReactNode

  • 默认值:无

  • 说明:主体

6. header

  • 类型:string|ReactNode

  • 默认值:无

  • 说明:头部

  • 不传值,显示默认头部; 传节点,自定义头部内容;传null,不显示头部;

7. footer

  • 类型:string|ReactNode

  • 默认值:无

  • 说明:底部

  • 不传值,显示默认底部; 传节点,自定义底部内容;传null,不显示底部;

8. okText

  • 类型:string

  • 默认值:确认

  • 说明:确认按钮文字

9. cancelText

  • 类型:string

  • 默认值:取消

  • 说明:取消按钮文字

10. picPath

  • 类型:string|ReactNode

  • 默认值:无

  • 说明:图片路径

11. style

  • 类型:object

  • 默认值:无

  • 说明:设置浮层的样式

12. centered

  • 类型:bool

  • 默认值:false

  • 说明:垂直居中展示 Modal

实现一款Modal.js

import React from 'react';
import PropTypes from 'prop-types';
import { createPortal } from 'react-dom';
const classNames = require('classnames');
import Button from '../../components/Button/Button.js';
import './Modal.scss';

/**
* Modal 模态对话框
*/

class Modal extends React.Component {
    // 入参类型检查
    static propTypes = {
        // 是否可见(true表示可见)
        visible: PropTypes.bool,
        // 类型
        type: PropTypes.string,
        // 宽度
        width: PropTypes.oneOfType([
            PropTypes.string,
            PropTypes.number,
        ]),
        // 确认按钮回调函数
        onOk: PropTypes.func,
        // 关闭按钮回调函数
        onCancel: PropTypes.func,
        // 确认按钮文案
        okText: PropTypes.string,
        // 关闭按钮文案
        cancelText: PropTypes.string,
        // 点击蒙层是否允许关闭
        maskClosable: PropTypes.bool,
        // 头部内容,当不需要默认头部时,可以设为 header={null}
        header: PropTypes.node,
        // 底部内容,当不需要默认底部按钮时,可以设为 footer={null}
        footer: PropTypes.node,
        // 可用于设置浮层的样式,调整浮层位置等
        style: PropTypes.object,
        // 是否垂直居中显示(true表示居中显示)
        centered: PropTypes.bool,
    };

    // 入参默认值
    static defaultProps = {
        visible: false,
        type: '',
        width: '480',
        onOk: () => {},
        onCancel: () => {},
        okText: '确认',
        cancelText: '取消',
        maskClosable: true,
        centered: false,
        prefixCls: 'modal',
    }

    constructor(props) {
        super(props);
        this.handleOk = this.handleOk.bind(this);
        this.handleCancel = this.handleCancel.bind(this);

        this.node = document.createElement('div');
        // this.node.className = 'modal-div';
        document.body.appendChild(this.node);
    }

    componentWillReceiveProps(props) {
        // console.log(props);
    }

    // 点击确认,更新modal中的 visible 状态
    handleOk() {
        const { onOk } = this.props;
        onOk && onOk();
    }

    // 点击取消,更新modal中的 visible 状态
    handleCancel() {
        const { onCancel } = this.props;
        onCancel && onCancel();
    }

    render() {
        const {
            visible,
            type,
            width,
            title,
            children,
            header,
            footer,
            okText,
            cancelText,
            picPath,
            style,
            centered,
            prefixCls
        } = this.props;

        var modalClass = classNames(prefixCls, {
            [`${prefixCls}-${type}`]: type
        }),
        iconClass1 = classNames('icon', {
            [`icon-${type}`]: type
        });

    let iconClass2 = '';
    switch (type) {
        case 'confirm':
            iconClass2 = 'icon-bangzhu';
            break;
        case 'info':
            iconClass2 = 'icon-xinxi-xianxing';
            break;
        case 'success':
            iconClass2 = 'icon-jieguoye';
            break;
        case 'error':
            iconClass2 = 'icon-guanbishibaixianxing';
            break;
        case 'warning':
            iconClass2 = 'icon-xinxi-xianxing';
            break;
        default:
            break;
    }

    const defaultHeader = (
        <div className="modal-header">
            <div className="modal-title">{title}</div>
        </div>
    );

    const defaultFooter = (
        <div className="modal-footer">
            <Button type="plain" onClick={this.handleCancel}>{cancelText}</Button>
            <Button type="primary" onClick={this.handleOk}>{okText}</Button>
        </div>
    );


    if (!visible) {
        return null;
    }

    return (
        createPortal(
            <div className="modal-root">
                <div className="modal-mask" onClick={this.props.maskClosable ? this.handleCancel : () => {}}></div>
                <div className={`modal-wrap ${!!centered ? "modal-centered" : ""}`}>
                    <div className={modalClass} style={{...style, ...{width: parseFloat(width)+'px'}}}>
                        <div className="modal-content">
                            {
                            !type && (
                            <div className="modal-close" onClick={this.handleCancel}>
                                <i className="icon icon-guanbi-xiao"></i>
                            </div>
                            )
                            }
                            {!type && header === undefined ? defaultHeader : header}
                            <div className="modal-body">
                                {!type && children}
                                {
                                type && (
                                <div className={`modal-confirm-${type}`}>
                                    <div className="modal-confirm-body">
                                        <div className="modal-confirm-title">
                                            <i className={`${iconClass1} ${iconClass2}`}></i>{title}
                                        </div>
                                        <div className="modal-confirm-content">{children}</div>
                                    </div>
                                    <div className="modal-confirm-btns">
                                        {type == "confirm" && (<Button type="plain" onClick={this.handleCancel}>{cancelText}</Button>)}
                                        <Button type="primary" onClick={this.handleOk}>{okText}</Button>
                                    </div>
                                </div>
                                )
                                }
                                {type == "picview" && (<img src={picPath} />)}
                                </div>
                                {!type && footer === undefined ? defaultFooter : footer}
                            </div>
                        </div>
                    </div>
                </div>, //塞进传送门的JSX
                this.node //传送门的另一端DOM node
            )

        );
    }
}

export default Modal;

「欢迎在评论区讨论」

希望看完的朋友可以给个赞,鼓励一下