「✍ React Hook 封装一个Modal ?」

背景

最近在和同事合作一个需求,其中很多模块都需要一个相同风格的简单弹窗组件,而项目中引入的antd-mobile无法直接满足需求,最后决定由我来做一次二次封装。

开始

一开始我的理解是封装一层样式,新增一些特有的属性,其他的props透传就完事了,于是有了下面的组件
CommonModal

import React from 'react'
import Modal, { ModalProps } from 'antd-mobile'

interface IProps extends ModalProps {
  content: string
  commonModalClassName?: string
}

const CommonModal: React.FC<IProps> = (props) => {
  const { commonModalClassName = '', content = '', ...resProps} = props
  return (
     <div className={`common-modal ${ commonModalClassName }`}>
       <Modal {...resProps}>
         <p className="common-modal-content">{ content }</p>
       </Modal>
     </div>
  )
}
复制代码

看起来简单粗暴没毛病。

使用Hook优化

功能已经实现,但同事用的时候反馈,作为一个简单的弹窗,每次用的时候还需要定义好state,还要引入CommonModal组件, 比较麻烦

const [visible, setVisible] = useState(false)
const [content, setContent] = useState(false
...
复制代码

这时我想起来antd-mobileModal可以直接调用Modal.alert来唤起弹窗,对于多处复用的弹窗,像这样封装一个可执行的方法是不错的。先看看antd-mobile的实现。

export default function alert(){
  const div: any = document.createElement('div');
  document.body.appendChild(div);

  function close() {
    ReactDOM.unmountComponentAtNode(div);
    if (div && div.parentNode) {
      div.parentNode.removeChild(div);
    }
  }
  
  ReactDOM.render(
    <Modal
      visible
      transparent
      title={title}
      transitionName="am-zoom"
      closable={false}
      maskClosable={false}
      footer={footer}
      maskTransitionName="am-fade"
      platform={platform}
      wrapProps={{ onTouchStart: onWrapTouchStart }}
    >
      <div className={`${prefixCls}-alert-content`}>{message}</div>
    </Modal>,
    div,
  );
}
复制代码

贴了部分核心代码,其实就是用ReactDOM来渲染弹窗,用dom.parentNode.removeChild来卸载弹窗。

用Hook实现

useCommonModal

import React from 'react'
import ReactDOM from 'react-dom'
import CommonModal, { CommonModalProps } from './CommonModal'

const CommonModal: React.FC<CommonModalProps> = ({...props}) => <CommonModal {...props} />

type IProps = Pick<CommonModalProps, 'title' | 'content' | 'btnText'>

const ModalClassName = 'hook-modal'

const useCommonModal = (props:IProps) => {

  const close = useCallback(()=>{
     let dom = document.getElementByClassName('hook-modal')[0]
     ReactDOM.unmountComponentAtNode(dom)
     if(dom && dom.parentNode){
       dom.parentNode.removeChild(dom);
     }
  },[])
  
  const show = useCallback(()=>{
    const Root = document.body
    let dom = document.getElementByClassName('hook-modal')[0]
    if(!dom){
      dom = document.createElement('div')
      dom.className = ModalClassName
      Root.appendChild(dom)
    }
    
    ReactDOM.render(
     <CommonModal {...props}/>
    , dom)
    // 等价于
    // const ele = React.createElement(CommonModal, { ...props}, null)
    // ReactDOM.render(ele, dom)
  },[])
  
  return {
    close,
    show
  }
}
复制代码

使用

const { show, close } = useCommonModal({
  title: '',
  content: '',
  btnText: '',
})
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享