在 Ant Design(简称 Antd) 组件库中弹窗的使用频率很高,Antd 提供的Modal组件一般用法如下:
import React, { useState } from 'react';
import { Button, Modal } from 'antd';
const App: React.FC = () => {
const [isModalOpen, setIsModalOpen] = useState(false);
const showModal = () => {
setIsModalOpen(true);
};
const handleOk = () => {
setIsModalOpen(false);
};
const handleCancel = () => {
setIsModalOpen(false);
};
return (
<>
<Button type="primary" onClick={showModal}>
Open Modal
</Button>
<Modal title="Basic Modal" open={isModalOpen} onOk={handleOk} onCancel={handleCancel}>
<p>Some contents...</p>
<p>Some contents...</p>
<p>Some contents...</p>
</Modal>
</>
);
};
上面使用方式存在几个问题:
- 弹窗与所属组件的状态混在一起,容易依赖组件内部的状态,导致其他地方不易复用
- 使用时需要关注弹窗的渲染位置,并主动控制弹窗的显示和隐藏细节
- 弹窗内容的渲染时机不好控制(需要额外处理),例如实现仅当弹窗可见时,才动态加载内容
我认为一个好用的弹窗我认为应该具备几个特点:
- 支持命令式调用,类似
window.alert()
这种方式(使用方无需关注弹窗的声明位置) - 支持与弹窗的双向交互,即提供参数控制弹窗的渲染,同时弹窗关闭时能从中获取所需数据(这个在表单场景常见)
- 弹窗的状态与其所在组件树隔离(避免状态污染)
Antd 提供的 Modal.method() 很好的处理了第 1 和 3 个问题,使用起来很简单,只需一行代码即可。
Modal.confirm({
title: '确认?',
content: <MyModalContent>,
onOk () {
// do something
}
})
但是Modal.method()
并未解决第 2 个问题,其内容展示后就与当前上下文脱离联系了,为了实现所期望的弹窗,下面我尝试了几种封装方式,试图解决这个问题。