封裝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