封装React AntD的dialog弹窗组件

前一段时间分享了基于vue和element所封装的弹窗组件(封装Vue Element的dialog弹窗组件),今天就来分享一个基于react和antD所封装的弹窗组件,反正所使用的技术还是那个技术,情况还是那个情况。只是基于vue所封装的弹窗组件和基于react所封装的弹窗组件还是有很大差别的。一样的是封装的思想和思路,不一样的是实现的技术。

至于所用到的技术,还是跟之前分享的有关react组件所用到的技术差不多,无非就是react hooks、函数式组件等。实现思路的话,还是在父组件中打开弹窗,关闭弹窗的操作就交给所封装的弹窗组件,不采用那种所谓的给子组件传一个关闭弹窗的函数,然后让子组件来触发,触发后再由父组件将关闭弹窗的参数传给子组件。这种实现思路可以是可以,就是太绕了,实现起来也麻烦不是?而且每次使用这个弹窗组件时,还得在每个父组件中都写一个关闭弹窗的方法,不累吗?封装的目的是什么?一是统一样式和功能,二是高复用性,三是少写代码,降低开发量,提高开发效率。在我们国家不是有一句很流行的话吗?“让专业的人干专业的事儿”,比如让姚明出任篮协主席,让那个所谓“不懂球的胖子”出任乒协主席,就是这个理儿。

当然有人可能会说如果将关闭弹窗的操作只交给弹窗来做的话,那么万一要在关闭弹窗后再执行某个操作呢,比如弹窗中嵌套了表单组件,如果有表单正则验证没通过,会在表单输入框下边有一个红色的文字提示,此时若关闭弹窗,下次再打开弹窗时会发现那个红色的文字提示还在,这个问题咋解决?总不能在关闭弹窗的函数中加入一个重置表单的方法吧,那如果还有其他需求呢,是不是也要在关闭弹窗的函数中再加入一个方法呢?如果其他需求不需要这样的方法呢?很好,有这样的疑问,说明你考虑问题很全面,哈哈哈哈哈哈,且这样的需求也是实实在在存在的,但依然有很好的解决办法,那就是充分利用弹窗关闭后的回调方法。

关于弹窗关闭后的回调方法,antD和element的弹窗组件都有这个方法,只是element的弹窗组件在关闭后有两个回调:

  • close:Dialog关闭的回调

  • closed:Dialog关闭动画结束时的回调

对于这两个回调,我都拿来试了一把,发现在一般情况下它俩的效果一样,那么官方对第二个回调给的解释是关闭动画结束时的回调,我就有点懵逼了。有兴趣的大佬或已经尝试过的大神别喷我,您就指着我的鼻子,居高临下地告诉我该怎么玩这个就可以了,我会虚心接受的。

antD在关闭弹窗后也给了一个回调afterClose,官方的解释是Modal完全关闭后的回调,但人家就只给了这一个关闭后的回调。好吧,就这个回调已经足够我们实现我们的需求了。我们把那些需要在关闭弹窗后再做的一些事情都放在这个回调中,然后把这个回调从父组件再传给所封装的弹窗子组件就完事了。如果你不传,代表你没有这方面的需求,对弹窗的正常工作又没有丝毫的影响,这岂不是很香吗?

说了这么多,光说不练假把式,那就来看具体实现呗。

照例还是先来张效果图:

1、所封装的弹窗组件dialog.js

import React, { useState, useImperativeHandle } from 'react'
import PropTypes from 'prop-types'
import { Modal } from 'antd';

let ok = () => {};
const DialogCom = ({btnTxt = ['取消', '确定'], children, cRef, autoClose = true, ...reset}) => {
  const [visible, setVisible] = useState(false);

  const open = cb => {
    setVisible(true);
    ok = cb;
  }

  useImperativeHandle(cRef, () => ({
    open: cb => open(cb),
  }));

  const handleCancel = () => {
    setVisible(false);
  }

  const handleOk = () => {
    autoClose && setVisible(false);
    ok(handleCancel);
  }

  return <Modal
    {...reset}
    maskClosable={false}
    visible={visible}
    onOk={handleOk}
    onCancel={handleCancel}
    okText={btnTxt[1]}
    cancelText={btnTxt[0]}
  >
    {children}
  </Modal>
}

DialogCom.propTypes = {
  btnTxt: PropTypes.array,
  children: PropTypes.any.isRequired,
  cRef: PropTypes.object.isRequired,
  autoClose: PropTypes.bool,
}

export default DialogCom

以上代码中的具体实现思路这里就不再做过多的介绍了,可以移步封装Vue Element的dialog弹窗组件这里。封装Vue Element的dialog弹窗组件这篇博文已经对实现的思路做了详细的介绍了。

2、使用方法:

import React, { useRef } from 'react'
import { Button } from 'antd';
import Dialog from './dialog'

const DialogDemo = () => {
  const childRef = useRef();

  const open = () => {
    childRef.current.open(cancel => {
      // cancel();
      console.log('打开')
    });
  }

  const resetForm = () => {
    // 我是用来重置表单的方法
  }

  const config = {
    title: '提示',
    btnTxt: ['关闭', '提交'],
    centered: true,
    width: '400px',
    afterClose: resetForm, // Modal完全关闭后的回调
  }

  return <>
    <Button type="primary" onClick={open}>打开弹窗</Button>
    <Dialog {...config} cRef={childRef}>
      <p>我是弹窗</p>
      <p>我是弹窗</p>
    </Dialog>
  </>
}

export default DialogDemo

使用方法中的代码这里也不再做介绍了,封装Vue Element的dialog弹窗组件这篇博文也介绍的很详细了。

最后还是再贴一下本次封装所用到的各个包的版本:

react: 16.8.6,

react-dom: 16.8.6,

react-router-dom: 5.0.0,

antd: 4.3.5,

@babel/core: 7.4.4,

babel-loader: 8.0.5