Refine v5 useModalForm 完全指南:在 Ant Design 弹窗中构建 create / edit / clone 表单
2026/9/12 21:20:17 网站建设 项目流程

Refine v5 useModalForm 完全指南:在 Ant Design 弹窗中构建 create / edit / clone 表单

【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine

useModalForm是 Refine v5 中@refinedev/antd包提供的高阶表单 Hook,用于把 CRUD 表单放进 Ant Design<Modal>的全部能力,并额外返回弹窗的modalProps、开关状态与显隐控制函数,让你用最少样板代码实现"点击按钮 → 弹出 Modal → 提交表单 → 自动关闭并刷新列表"的完整交互闭环。读完本文,你将掌握 create / edit / clone 三种模式的标准写法、全部可选参数与返回值,并能结合源码理解其内部实现原理。

useModalForm 是什么

useModalForm允许你在<Modal>扩展而来,因此useForm的所有特性(数据获取、提交、mutation mode、redirect 等)在useModalForm中均可直接使用。

从源码可以确认两者的继承关系:在 useModalForm.ts 中,useModalForm直接调用了useForm,并把useForm返回的结果通过...useFormProps透传出去;其返回值类型UseModalFormReturnTypeUseFormReturnType剔除saveButtonPropsdeleteButtonProps后再叠加弹窗专用字段(opencloseshowmodalProps等)得到的。

const { modalProps, formProps, show, close, formLoading, } = useModalForm<IPost>({ action: "create", });

核心用法非常简单:modalProps直接展开到<Modal>上,formProps直接展开到<Form>上,show(id?)负责打开弹窗。

Usage:三种内置动作模式

下面通过"create""edit""clone"三个示例展示useModalForm的典型用法。三个模式都由action属性驱动,配合表格场景中的按钮触发。

create:新建记录

创建模式下,点击列表上方的创建按钮即可打开空白表单弹窗。通过List组件的createButtonProps.onClick调用show()来触发弹窗:

import React from "react"; import { List, useModalForm, useTable } from "@refinedev/antd"; import { Form, Input, Modal, Select, Table } from "antd"; const PostList: React.FC = () => { const { tableProps } = useTable<IPost>(); const { modalProps: createModalProps, formProps: createFormProps, show: createModalShow, } = useModalForm<IPost>({ action: "create", }); return ( <> <List // createButtonProps 让我们可以在表格上方创建并管理一个按钮, // 点击按钮时触发 <Modal> 显示 createButtonProps={{ onClick: () => { createModalShow(); }, }} > <Table {...tableProps} rowKey="id"> <Table.Column dataIndex="id" title="ID" /> <Table.Column dataIndex="title" title="Title" /> <Table.Column dataIndex="status" title="Status" /> </Table> </List> <Modal {...createModalProps}> <Form {...createFormProps} layout="vertical"> <Form.Item label="Title" name="title" rules={[{ required: true }]} > <Input /> </Form.Item> <Form.Item label="Status" name="status" rules={[{ required: true }]} > <Select options={[ { label: "Published", value: "published" }, { label: "Draft", value: "draft" }, { label: "Rejected", value: "rejected" }, ]} /> </Form.Item> </Form> </Modal> </> ); }; interface IPost { id: number; title: string; status: "published" | "draft" | "rejected"; }

edit:编辑记录

Refine 不会自动为列表中的每行记录添加<EditButton />,需要你在表格的 Actions 列手动放置。点击<EditButton />时通过show(record.id)传入记录 id,弹窗中的编辑表单即可据此拉取该条记录的数据。

const { modalProps: editModalProps, formProps: editFormProps, show: editModalShow, } = useModalForm<IPost>({ action: "edit", warnWhenUnsavedChanges: true, });
<Table.Column<IPost> title="Actions" dataIndex="actions" key="actions" render={(_, record) => ( <Space> <EditButton hideText size="small" recordItemId={record.id} onClick={() => editModalShow(record.id)} /> </Space> )} />

注意:必须把记录的"id"传给showeditclone表单都需要依赖该 id 获取记录数据。源码中的handleShow也印证了这一约束——当action"edit""clone"时,只有传入(或已存在)id 才会真正打开弹窗,见 useModalForm.ts。

clone:克隆记录

克隆模式用于基于已有记录快速创建一条新数据。同样需要手动在列表中加入<CloneButton />并传入记录 id:

const { modalProps: cloneModalProps, formProps: cloneFormProps, show: cloneModalShow, } = useModalForm<IPost>({ action: "clone", });
<Table.Column<IPost> title="Actions" dataIndex="actions" key="actions" render={(_, record) => ( <Space> <CloneButton hideText size="small" recordItemId={record.id} onClick={() => cloneModalShow(record.id)} /> </Space> )} />

clone 模式会预填原记录的数据,提交时走 create 逻辑,生成一条全新的记录。

Properties:配置弹窗表单的开关与默认值

useModalForm继承useForm的全部 props(详见 use-form 文档的 Properties 小节),并额外提供以下弹窗专属配置。所有默认值都能在 useModalForm.ts 的解构赋值中直接找到依据。

syncWithLocation

syncWithLocationtrue时,弹窗的可见状态以及记录的id会与 URL 同步,默认值为false。这带来两个实际收益:刷新页面后弹窗状态不丢失,且弹窗状态可以作为可分享的链接。

该属性也可以写成对象形式{ key: string; syncId?: boolean }来定制 URL 查询参数的 key;只有当syncIdtrue时,id才会同步到 URL:

const modalForm = useModalForm({ syncWithLocation: { key: "my-modal", syncId: true }, });

从源码看,未指定key时默认的查询参数名为`modal-${identifier}-${action}`(例如modal-posts-edit),内部通过useParsed读取 URL 参数、通过useGo{ open: true, id }写回 URL,见 useModalForm.ts。测试用例should meta[syncWithLocationKey] overrided by default也验证了syncWithLocation: truegetOne请求会携带meta: { "modal-posts-edit": undefined },见 index.spec.tsx。

defaultFormValues

表单的默认值,用于预填需要展示的数据:

useModalForm({ defaultFormValues: { title: "Hello World", }, });

它也可以传入一个 async 函数来异步获取默认值,加载状态通过返回的defaultFormValuesLoading跟踪:

const { defaultFormValuesLoading } = useModalForm({ defaultFormValues: async () => { const response = await fetch("https://my-api.com/posts/1"); const data = await response.json(); return data; }, });

🚨 当action"edit""clone"时,与异步defaultFormValues之间可能产生竞态条件,此时表单值将是最后一个完成操作的结果。

defaultVisible

设为true时弹窗默认显示,默认值为false

const modalForm = useModalForm({ defaultVisible: true, });

autoSubmitClose

提交成功后是否自动关闭弹窗,默认值为true

const modalForm = useModalForm({ autoSubmitClose: false, });

autoResetForm

提交成功后是否重置表单,默认值为true

const modalForm = useModalForm({ autoResetForm: false, });

autoResetFormWhenClose

弹窗关闭时是否重置表单,默认值为true

const modalForm = useModalForm({ autoResetFormWhenClose: false, });

warnWhenUnsavedChanges

设为true后,当用户带着未保存的修改离开页面时会弹出警告,防止误操作丢失数据,默认值为false。也可以在<Refine>组件中统一配置:

const modalForm = useModalForm({ warnWhenUnsavedChanges: true, });

源码中该警告通过window.confirm弹出,文案默认是 "Are you sure you want to leave? You have unsaved changes.",支持通过 i18n 的warnWhenUnsavedChanges键翻译,见 useModalForm.ts。

overtimeOptions

当请求耗时过长时,可以通过overtimeOptions显示加载提示。interval是毫秒级的检查间隔,onInterval是每个间隔触发的回调。Hook 返回overtime对象,elapsedTime为已耗时毫秒数,请求完成后变为undefined

const { overtime } = useModalForm({ //... overtimeOptions: { interval: 1000, onInterval(elapsedInterval) { console.log(elapsedInterval); }, }, }); console.log(overtime.elapsedTime); // undefined, 1000, 2000, 3000 4000, ... // 实际使用方式: { elapsedTime >= 4000 && <div>this takes a bit longer than expected</div>; }

autoSave

如果希望在用户编辑表单后延时自动保存,可以启用autoSave.enabled。默认情况下 autoSave 不会使查询失效,但可以通过invalidateOnUnmountinvalidateOnClose在卸载或关闭时让查询失效。它还支持onMutationSuccessonMutationError回调,回调中可用isAutoSave参数判断 mutation 是否由 autoSave 触发。

autoSave 只作用于edit模式:编辑数据时改动会自动保存;而创建新数据时仍需手动提交。

enabled

启用 autoSave,默认false

useModalForm({ autoSave: { enabled: true, }, });
debounce

设置 autoSave 的防抖时间(毫秒),默认1000

useModalForm({ autoSave: { enabled: true, debounce: 2000, }, });
onFinish

在数据发送到服务器之前修改数据:

useModalForm({ autoSave: { enabled: true, onFinish: (values) => { return { foo: "bar", ...values, }; }, }, });
invalidateOnUnmount

Hook 卸载时使当前资源关联的listmanydetail查询失效,默认false。也可以通过invalidates属性选择要失效的查询类型:

useModalForm({ autoSave: { enabled: true, invalidateOnUnmount: true, }, });
invalidateOnClose

弹窗关闭时使当前资源关联的listmanydetail查询失效,默认false

useModalForm({ autoSave: { enabled: true, invalidateOnClose: true, }, });

invalidateOnClose的实现位于handleClose中:当autoSaveProps.status === "success"时,调用invalidate({ id, invalidates, dataProviderName, resource })使缓存失效,见 useModalForm.ts。

Return Values:Hook 返回什么

useModalForm返回useForm的所有返回值,外加与<Modal>协作所需的额外值。

formProps

管理<Form>状态与动作所必需的 props,底层来自useForm。它包含管理 Ant Design<Form>的各类属性(onValuesChangeinitialValuesonFieldsChangeonFinish等)。

注意onFinishformProps.onFinish的区别useModalForm直接返回的onFinishuseFormonFinish一致;而在弹窗场景下,提交后关闭弹窗、重置字段是必须的,因此formProps.onFinishonFinish做了扩展,在底层额外处理了弹窗关闭与字段清空。如果你需要在提交前定制数据,建议使用formProps.onFinish,把提交后的收尾工作交给它处理。

源码中formProps.onFinish的执行顺序是:先await onFinish(values)完成提交,再根据autoSubmitClose决定是否close(),最后根据autoResetForm决定是否form.resetFields(),见 useModalForm.ts。

modalProps

<Modal>中统一组装:

属性说明默认值
title弹窗标题,基于资源与 action 值自动生成(如 "Edit test",见测试用例 index.spec.tsx)由资源名与动作组合
okText弹窗内"提交"按钮的文本"Save"
cancelText弹窗内"取消"按钮的文本"Cancel"
width弹窗宽度1000px
forceRender是否立即渲染弹窗(而非懒渲染)true
okButtonProps"提交"按钮所需的所有 props(disabledloading等),点击okButtonProps.onClick会触发form.submit()基于formLoading派生
onOk提交弹窗内<Form>的函数,适合手动提交表单
onCancel关闭弹窗的函数(等同于close),适合手动关闭

open

弹窗当前可见状态(boolean),默认值取决于defaultVisible

close

手动关闭弹窗的函数。内部会依次处理:autoSave 失效、warnWhenUnsavedChanges确认、清空id、关闭弹窗、按autoResetFormWhenClose重置字段。配合手动提交的典型写法:

const { close, modalProps, formProps, onFinish } = useModalForm(); const onFinishHandler = async (values) => { // await `onFinish` 对未保存更改提示、查询失效、重定向等功能至关重要 // 如果使用 formProps.onFinish,它会在内部自动调用 close await onFinish(values); close(); }; return ( <Modal {...modalProps}> <Form {...formProps} onFinish={onFinishHandler} layout="vertical"> <Form.Item label="Title" name="title"> <Input /> </Form.Item> </Form> </Modal> );

submit

手动提交表单的函数。适合自定义弹窗 footer 按钮的场景:

const { modalProps, formProps, submit } = useModalForm(); return ( <Modal {...modalProps} footer={[ <Button key="submit" type="primary" onClick={submit}> Submit </Button>, ]} > <Form {...formProps} layout="vertical"> <Form.Item label="Title" name="title"> <Input /> </Form.Item> </Form> </Modal> );

show

打开弹窗的函数,可接收可选的记录id

const { modalProps, formProps, show } = useModalForm(); return ( <> <Button type="primary" onClick={() => show()}> Show Modal </Button> <Modal {...modalProps}> <Form {...formProps} layout="vertical"> <Form.Item label="Title" name="title"> <Input /> </Form.Item> </Form> </Modal> </> );

overtime

overtime对象:elapsedTime为已耗时毫秒数,请求完成后变为undefined

const { overtime } = useModalForm(); console.log(overtime.elapsedTime); // undefined, 1000, 2000, 3000 4000, ...

autoSaveProps

启用autoSave后,Hook 返回包含dataerrorstatus三个属性的 mutation 结果对象,status取值包括"loading" | "error" | "idle" | "success"

defaultFormValuesLoading

defaultFormValues是 async 函数时,在该函数 resolve 之前此值为true

FAQ:如何在提交前修改表单数据

一个常见需求:把用户填写的namesurname两个字段,合并成fullName再发送给 API。做法是利用formProps.onFinish拦截提交:

import { Modal, useModalForm } from "@refinedev/antd"; import { Form, Input } from "antd"; import React from "react"; export const UserCreate: React.FC = () => { const { formProps, modalProps } = useModalForm({ action: "create", }); const handleOnFinish = (values) => { formProps.onFinish?.({ fullName: `${values.name} ${values.surname}`, }); }; return ( <Modal {...modalProps}> <Form {...formProps} onFinish={handleOnFinish} layout="vertical"> <Form.Item label="Name" name="name"> <Input /> </Form.Item> <Form.Item label="Surname" name="surname"> <Input /> </Form.Item> </Form> </Modal> ); };

由于走的是formProps.onFinish,提交成功后弹窗关闭、表单重置等收尾逻辑依然由 Hook 自动完成。

API Reference

Type Parameters

参数说明类型默认值
TQueryFnData查询函数返回的结果数据,继承BaseRecordBaseRecordBaseRecord
TError继承HttpError的自定义错误对象HttpErrorHttpError
TVariables提交参数的值{}
TDataselect函数返回的结果数据,继承BaseRecord,未指定时默认取TQueryFnDataBaseRecordTQueryFnData
TResponsemutation 函数返回的结果数据,继承BaseRecord,未指定时默认取TDataBaseRecordTData
TResponseError继承HttpError的自定义错误对象,未指定时默认取TErrorHttpErrorTError

Return Value

Key说明类型
show打开弹窗的函数(id?: BaseKey) => void
formProps管理表单组件所需的 propsFormProps
modalProps管理弹窗组件所需的 propsModalProps
formLoading表单加载状态boolean
submit提交方法,参数为表单字段值() => void
open弹窗是否打开boolean
close关闭弹窗的函数() => void
defaultFormValuesLoading默认表单值的加载状态boolean
formAnt Design 表单实例FormInstance<TVariables>
idedit 动作对应的记录 idBaseKey \| undefined
setIdid的 setterDispatch<SetStateAction<BaseKey \| undefined>>
query记录查询的结果QueryObserverResult<{ data: TData }>
mutation提交表单触发的 mutation 结果UseMutationResult<{ data: TData }, TError, { resource: string; values: TVariables; }, unknown>
overtime超时加载 props{ elapsedTime?: number }
autoSaveProps自动保存 props{ data: UpdateResponse<TData> \| undefined, error: HttpError \| null, status: "loading" \| "error" \| "idle" \| "success" }

结合源码理解内部行为

默认值定义defaultVisible = falseautoSubmitClose = trueautoResetForm = trueautoResetFormWhenClose = true,全部在 useModalForm.ts 的入口解构中声明,与文档描述完全一致。

mutationMode 与关闭时机:测试用例验证了不同 mutation mode 下弹窗的关闭行为——pessimistic模式会等待 mutation 成功后才关闭弹窗,而optimistic/undoable模式提交后立即关闭,见 index.spec.tsx。formProps.onFinishawait onFinish(values)正是保证该时序的关键。

基础弹窗状态管理useModalForm底层复用了@refinedev/antduseModal(见 useModal/index.tsx),它把核心useModalvisible映射为 Ant Design Modal 的open,并接管onCancel默认关闭行为;useModalForm再在其上叠加handleShow/handleClose完成 id 注入、未保存警告、自动重置等增强逻辑。

真实可运行示例:仓库中的 form-antd-use-modal-form 示例同时演示了 create 与 edit 两个弹窗、syncWithLocation以及独立的 Show 弹窗,完整代码见 examples/form-antd-use-modal-form/src/pages/posts/list.tsx。可通过以下命令在本地运行:

npm create refine-app@latest -- --example form-antd-use-modal-form

小结

useModalForm把 Refine 的表单数据流与 Ant Design 的 Modal 弹窗无缝衔接:action: "create" | "edit" | "clone"决定表单行为,syncWithLocation让弹窗状态可被 URL 记忆,autoSave为编辑场景提供防抖自动保存,autoSubmitClose/autoResetForm/autoResetFormWhenClose三个开关精确控制提交与关闭后的收尾动作。配合源码中明确的默认值与测试用例,你可以放心地把它当作弹窗 CRUD 的标准方案。

【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询