ant-design Modal.confirm 静态确认框实战指南:Promise 延迟关闭与按钮定制
【免费下载链接】ant-designAn enterprise-class UI design language and React UI library项目地址: https://gitcode.com/gh_mirrors/ant/ant-design
导读
本文围绕 ant-design 中Modal.confirm()这一静态方法,讲解如何快速弹出确认框、如何让onOk/onCancel返回 Promise 以延迟关闭对话框,并深入源码剖析其底层实现机制。读完本文,你将掌握确认框的基础用法、异步关闭编排、按钮文案与类型定制、update/destroy引用操作,以及destroyAll、useModal等周边能力,能够直接在你的 React 项目中落地实践。
一、认识 Modal.confirm:一行代码弹出确认框
在 ant-design 的 Modal 组件文档 中,确认框是最高频的使用场景之一。Modal.confirm()是挂载在Modal上的静态方法,与Modal.info、Modal.success、Modal.error、Modal.warning并列,用于「在当前页面正中弹出一个浮层,承载相应操作」,避免跳转页面打断用户工作流。
官方示例(components/modal/demo/confirm.md)的核心说明只有一句话:使用confirm()可以快捷地弹出确认框,onCancel/onOk 返回 promise 可以延迟关闭。而完整示例代码位于 components/modal/demo/confirm.tsx,展示了 4 种典型用法,本文将以它为骨架逐层展开。
二、快速开始:基础确认框
最基本的confirm()调用只需传入标题、图标与内容,并挂上onOk/onCancel回调:
import { ExclamationCircleFilled } from '@ant-design/icons'; import { Button, Modal, Space } from 'antd'; const { confirm } = Modal; const showConfirm = () => { confirm({ title: 'Do you want to delete these items?', icon: <ExclamationCircleFilled />, content: 'Some descriptions', onOk() { console.log('OK'); }, onCancel() { console.log('Cancel'); }, }); }; const App: React.FC = () => ( <Space wrap> <Button onClick={showConfirm}>Confirm</Button> </Space> ); export default App;要点说明:
- 静态调用,无需在 JSX 中渲染:
confirm()内部会自行创建容器并渲染对话框(见 components/modal/confirm.tsx 中document.createDocumentFragment()与reactRender的调用),组件卸载时也无需手动清理。 - 默认图标:当不传
icon时,确认框会依据类型显示默认图标。从 ConfirmDialog.tsx 的ConfirmContent实现可以看到:type: 'confirm'与warning默认渲染<ExclamationCircleFilled />,info用<InfoCircleFilled />,success用<CheckCircleFilled />,error用<CloseCircleFilled />;传入icon: null则可以完全隐藏默认图标。 - 回调参数:与受控
<Modal>不同,静态方法中onOk/onCancel的回调签名是function(close),第一个参数是关闭函数(详见下文的 Promise 延迟关闭与官方 API 表)。
三、核心能力:onOk/onCancel 返回 Promise 延迟关闭
这是confirm()最强大的能力,官方文档特别点明:onCancel/onOk 返回 promise 对象可以延迟关闭对话框。示例showPromiseConfirm演示了典型场景——点击确定后先执行异步操作(如提交请求、删除数据),成功后再关闭:
const showPromiseConfirm = () => { confirm({ title: 'Do you want to delete these items?', icon: <ExclamationCircleFilled />, content: 'When clicked the OK button, this dialog will be closed after 1 second', onOk() { return new Promise((resolve, reject) => { setTimeout(Math.random() > 0.5 ? resolve : reject, 1000); }).catch(() => console.log('Oops errors!')); }, onCancel() {}, }); };3.1 行为规则
- resolve → 关闭:
onOk/onCancel返回的 Promise 被resolve时,对话框自动关闭; - reject → 不关闭:Promise 被
reject时,对话框保持打开,方便用户修正操作或重试; - 同步返回(非 Promise)→ 立即关闭:回调没有返回值或返回非 thenable 对象时,点击后立即关闭。
3.2 源码级原理解析
延迟关闭的实现并不在confirm()本身,而在于底部按钮组件使用的通用异步按钮组件ActionButton(components/_util/ActionButton.tsx)。确认框的确定/取消按钮分别由 ConfirmOkBtn.tsx 与 ConfirmCancelBtn.tsx 渲染,两者都基于ActionButton。
关键逻辑在ActionButton的onClick与handlePromiseOnOk中:
actionFn(即你传入的onOk)被调用后,返回值会经过isThenable判断(!!thing?.then);- 若返回 Promise,则进入
handlePromiseOnOk:先调用setLoading(true)让确定按钮进入 loading 态,防止重复点击; - Promise
resolve时,setLoading(false)并调用onInternalClose(...args)触发关闭流程; - Promise
reject时,同样结束 loading 与防抖标记,但不关闭对话框;若存在isSilent模式则吞掉异常,否则Promise.reject(e)继续向上抛出(参考 ant-design issue #6183 的约定)。
由此可以看出,示例中onOk内setTimeout(Math.random() > 0.5 ? resolve : reject, 1000)的写法,正是利用这一机制模拟「一半概率成功关闭、一半概率校验失败保持打开」的真实业务场景;而.catch(() => console.log('Oops errors!'))则是为了吞掉 reject 分支,避免控制台出现未处理异常。
四、按钮定制:okText / okType / cancelText / okButtonProps
示例的showDeleteConfirm与showPropsConfirm展示了如何定制按钮文案与危险样式:
const showDeleteConfirm = () => { confirm({ title: 'Are you sure delete this task?', icon: <ExclamationCircleFilled />, content: 'Some descriptions', okText: 'Yes', okType: 'danger', cancelText: 'No', onOk() { console.log('OK'); }, onCancel() { console.log('Cancel'); }, }); }; const showPropsConfirm = () => { confirm({ title: 'Are you sure delete this task?', icon: <ExclamationCircleFilled />, content: 'Some descriptions', okText: 'Yes', okType: 'danger', okButtonProps: { disabled: true, // 将确定按钮置为禁用 }, cancelText: 'No', onOk() { console.log('OK'); }, onCancel() { console.log('Cancel'); }, }); };参数说明:
| 参数 | 说明 | 默认值 |
|---|---|---|
okText | 确认按钮文字 | 确定(依 locale 而定,如英文环境为OK) |
cancelText | 取消按钮文字 | 取消 |
okType | 确认按钮类型,可传 Button 的type(如danger、primary、dashed) | primary |
okButtonProps | 透传给确定按钮的完整 ButtonProps,可控制disabled、loading、size等 | - |
cancelButtonProps | 透传给取消按钮的 ButtonProps | - |
从源码看,okType默认值在 ConfirmOkBtn.tsx 中体现为okType || 'primary';而按钮文字若未显式传入,会从 locale 读取——ConfirmDialog.tsx 中okTextLocale = okText || (mergedOkCancel ? mergedLocale?.okText : mergedLocale?.justOkText),也就是说当okCancel为 false(只显示确定按钮的info类弹窗)时,按钮文字会退化为 locale 的justOkText(如「知道了」)。
五、完整 API:Modal.confirm 参数全表
Modal.confirm接收一个ModalFuncProps对象(类型定义见 components/modal/interface.ts),除上节按钮定制外,常用参数如下(默认值与说明依据 index.zh-CN.md 的Modal.method()小节):
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
title | 标题 | ReactNode | - |
content | 内容 | ReactNode | - |
icon | 自定义图标 | ReactNode | <ExclamationCircleFilled /> |
onOk | 点击确定回调,参数为关闭函数close;返回 Promise 时 resolve 正常关闭、reject 不关闭 | function(close) | - |
onCancel | 点击取消回调,同上;另外点击遮罩、右上角关闭按钮时也会触发(此时附带triggerCancel标记) | function(close) | - |
afterClose | Modal 完全关闭后的回调 | function | - |
okCancel | 是否显示取消按钮 | boolean | true(confirm 类型) |
autoFocusButton | 指定自动获得焦点的按钮 | null|ok|cancel | ok |
centered | 垂直居中展示 | boolean | false |
width | 宽度 | string | number | 416 |
mask | 是否展示遮罩 | boolean | true |
maskClosable | 点击蒙层是否允许关闭 | boolean | false(静态方法默认关闭) |
keyboard | 是否支持 Esc 关闭 | boolean | true |
closable | 是否显示右上角关闭按钮 | boolean | false(静态方法默认不显示) |
className | 容器类名 | string | - |
wrapClassName | 对话框外层容器类名 | string | - |
zIndex | 弹层 z-index | number | 1000 |
style | 设置浮层样式 | CSSProperties | - |
getContainer | 指定挂载节点,false挂载在当前 DOM | HTMLElement | () => HTMLElement | Selectors | false | document.body |
footer | 自定义底部,设为null可隐藏按钮区;5.9.0 起支持渲染函数 | ReactNode | function | - |
modalRender | 自定义渲染对话框 | (node) => ReactNode | - |
focusTriggerAfterClose | 关闭后是否聚焦触发元素 | boolean | true |
关于默认值的源码印证
- 宽度 416:见 ConfirmDialog.tsx 中
const width = props.width || 416;,这与普通<Modal>默认 520 不同,确认框更紧凑; - maskClosable 默认 false:
const maskClosable = props.maskClosable === undefined ? false : props.maskClosable;,即静态确认框默认点击遮罩不会关闭,避免误触丢失未完成的确认操作; - zIndex 自动取最高层:未显式传入
zIndex时,静态方法会使用token.zIndexPopupBase + CONTAINER_MAX_OFFSET(最大偏移量),保证确认框始终浮于普通弹层之上。
六、返回引用:update 更新与 destroy 销毁
confirm()调用后会返回一个引用,可以随时更新弹窗内容或主动销毁:
const modal = Modal.confirm({ title: '确认删除?', content: '此操作不可恢复' }); // 更新配置 modal.update({ title: '修改后的标题', content: '修改后的内容', }); // 4.8.0+ 支持传入函数式更新 modal.update((prevConfig) => ({ ...prevConfig, title: `${prevConfig.title}(新)`, })); // 主动销毁 modal.destroy();这一 API 签名在源码 confirm.tsx 中有明确体现:ModalFunc = (props) => { destroy: () => void; update: (configUpdate) => void }。其内部实现是:
update(configUpdate):若传入函数,则以当前配置为入参执行并合并结果;若传入对象,则浅合并进currentConfig,随后重新render;destroy()(即内部close):将open置为false并挂上afterClose,动画结束后卸载 React 子树并从destroyFns注册表中移除自身。
七、全局兜底:Modal.destroyAll 与 useModal
7.1 Modal.destroyAll
Modal.destroyAll()用于一次性销毁所有通过静态方法弹出的确认窗。官方文档给出的典型场景是路由监听:路由前进/后退时旧的确认框不会自动关闭,需要在路由变更时统一销毁,而无需逐个持有modal.destroy()引用。其实现见 components/modal/index.tsx:循环弹出destroyFns注册表中的所有关闭函数并逐一调用。
import { browserHistory } from 'react-router'; browserHistory.listen(() => { Modal.destroyAll(); });注意:
modal.destroy()适用于主动关闭(如用户点确定/取消后的兜底清理);路由这类被动场景建议使用destroyAll()。
7.2 Modal.useModal
静态方法无法读取 React Context。若确认框内需要使用主题、国际化等上下文,官方推荐Modal.useModal():它返回[modal, contextHolder],把contextHolder插入组件树后,通过modal.confirm(...)创建的弹窗即可获得该位置的完整上下文(示例见 components/modal/demo/hooks.tsx)。
const [modal, contextHolder] = Modal.useModal(); React.useEffect(() => { modal.confirm({ title: '来自 hooks 的确认框' }); }, []); return <div>{contextHolder}</div>;hooks 返回的modal.confirm除destroy、update外,还额外支持then链式调用与await语法,点击确定返回true、取消返回false:
const confirmed = await modal.confirm({ ... });八、底层渲染流程速览
为帮助理解confirm()的「魔法」,这里梳理一次完整调用链(源码均在 components/modal/confirm.tsx 与 components/modal/index.tsx):
Modal.confirm(props)内部调用confirm(withConfirm(props)),其中withConfirm只是把type: 'confirm'写入配置;confirm()创建document.createDocumentFragment()容器,把{ ...config, close, open: true }存入currentConfig,并注册close到destroyFns;- 通过
setTimeout异步调用reactRender(同步渲染会阻塞 React 事件,见 issue #23623 的注释说明),把ConfirmDialogWrapper挂载到 fragment 上; ConfirmDialogWrapper从ConfigProvider的全局配置读取prefixCls、iconPrefixCls、theme、direction、locale,最终由ConfirmDialog组合出确认框 UI(图标区、标题、内容、按钮区);- 点击确定/取消 →
ConfirmOkBtn/ConfirmCancelBtn→ActionButton执行onOk/onCancel,按第三节的 Promise 规则决定是否触发close。
这条链路也解释了为什么静态方法存在两个官方提示:一是getContainer: false不被支持(静态方法没有 context 环境,见ConfirmDialogWrapper中的 warning);二是 RTL 模式仅支持 hooks 用法。
九、实战建议与注意事项
- 异步操作优先返回 Promise:删除、提交等危险操作请在
onOk中返回 Promise,让确定按钮自动进入 loading 并防止重复提交;失败时 reject 保持弹窗,配合content中的错误提示引导用户重试。 - 危险操作使用
okType: 'danger':红色按钮能显著降低误操作概率,如示例中的删除任务场景。 okButtonProps.disabled做二次保险:当业务要求某些状态下禁止确认(如未勾选协议、表单未填完)时,用okButtonProps: { disabled: true }精确控制。- 静态方法拿不到 Context:需要 theme/locale 上下文时改用
Modal.useModal()+contextHolder。 - 关闭状态不会自动清空:与
<Modal />相同,若每次打开都希望是全新内容,需配合destroyOnClose或显式重建配置对象。
以上全部示例代码、类型定义与实现细节,均可直接在仓库中查阅:示例、实现、类型定义、确认框渲染、异步按钮逻辑 以及 组件文档。
【免费下载链接】ant-designAn enterprise-class UI design language and React UI library项目地址: https://gitcode.com/gh_mirrors/ant/ant-design
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考