
Refine useModalForm 完全指南在 Ant Design Modal 中构建 create / edit / clone 表单【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refineuseModalForm是 Refine 的 Ant Design 集成包refinedev/antdv3 时代为pankod/refine-antd提供的高阶表单 Hook用于在Modal弹窗内管理create、edit、clone三种表单流程。它直接返回 Ant DesignForm与Modal所需的全部 props让开发者无需手动编写开关状态、数据加载、提交与重置逻辑。读完本文你将掌握它的三种 action 用法、全部配置属性与返回值语义并理解其底层实现原理与测试契约可直接在管理后台项目中落地使用。useModalForm 是什么useModalForm允许你在一个Modal中管理表单并返回 Ant DesignForm和Modal两个组件的 props。它的核心优势在于表单的状态管理、数据获取、提交请求、弹窗开关全部由 Hook 托管你只需要把modalProps和formProps展开到对应的组件上即可。它被设计为useForm的扩展useModalFormhook is extended fromuseForm。这意味着你可以使用useForm的所有特性。也就是说useForm的action、resource、redirect、mutationMode、autoSave、successNotification、invalidates、dataProviderName等全部能力在useModalForm中都可用只是额外叠加了 Modal 的开合与联动逻辑。在代码层面这一继承关系非常直观。当前仓库中useModalForm的实现位于 packages/antd/src/hooks/form/useModalForm/useModalForm.ts它在函数体内先调用useForm(...)拿到{ form, formProps, id, setId, formLoading, onFinish, autoSaveProps }再通过useModal位于 packages/antd/src/hooks/modal/useModal/index.tsx管理弹窗的show / close / open状态最终把两者合并成一份完整的返回值。// useModalForm.ts 内部结构简化 const useFormProps useForm({ autoSave, invalidates, ...rest }); const { form, formProps, id, setId, formLoading, onFinish } useFormProps; const { show, close, modalProps } useModal({ modalProps: { open: defaultVisible } });版本提示v3.x 文档中的示例使用pankod/refine-antd包名在后续版本中包已更名并发布为refinedev/antd源码位于仓库 packages/antd 目录示例项目 examples/form-antd-use-modal-form 即使用新包名。API 形态保持一致。基本用法create / edit / clone 三种模式useModalForm通过action属性区分三种表单场景我们逐一来看。三者均以posts资源为例字段为id、title、status。create 模式创建模式下点击表格上方的按钮打开 Modal填写表单并提交即调用create数据提供器方法。关键在于把show挂到List的createButtonProps.onClick上让点击按钮时弹出 Modal。import React from react; import { IResourceComponentsProps } from pankod/refine-core; import { List, Table, Form, Select, Input, Modal, Space, EditButton, useTable, useModalForm, } from pankod/refine-antd; const PostList: React.FCIResourceComponentsProps () { const { tableProps } useTableIPost(); const { modalProps: createModalProps, formProps: createFormProps, show: createModalShow, } useModalFormIPost({ action: create, }); return ( List // createButtonProps 允许我们创建并管理表格上方的按钮 // 这段代码让点击按钮时 Modal 弹出。 createButtonProps{{ onClick: () { createModalShow(); }, }} Table {...tableProps} rowKeyid Table.Column dataIndexid titleID / Table.Column dataIndextitle titleTitle / Table.Column dataIndexstatus titleStatus / /Table /List Modal {...createModalProps} Form {...createFormProps} layoutvertical Form.Item labelTitle nametitle rules{[ { required: true, }, ]} Input / /Form.Item Form.Item labelStatus namestatus 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 模式编辑模式需要把show(record.id)挂到每条记录的EditButton上。当action: edit时show收到记录的id后Hook 会自动根据该id调用getOne获取数据并回填到表单通过initialValues注入提交时则调用update。import React from react; import { IResourceComponentsProps } from pankod/refine-core; import { List, Table, Form, Select, Input, Modal, Space, EditButton, useTable, useModalForm, } from pankod/refine-antd; const PostList: React.FCIResourceComponentsProps () { const { tableProps } useTableIPost(); const { modalProps: editModalProps, formProps: editFormProps, show: editModalShow, } useModalFormIPost({ action: edit, warnWhenUnsavedChanges: true, }); return ( List Table {...tableProps} rowKeyid Table.Column dataIndexid titleID / Table.Column dataIndextitle titleTitle / Table.Column dataIndexstatus titleStatus / Table.ColumnIPost titleActions dataIndexactions keyactions render{(_, record) ( Space EditButton hideText sizesmall recordItemId{record.id} onClick{() editModalShow(record.id)} / /Space )} / /Table /List Modal {...editModalProps} Form {...editFormProps} layoutvertical Form.Item labelTitle nametitle rules{[{ required: true }]} Input / /Form.Item Form.Item labelStatus namestatus rules{[{ required: true }]} Select options{[ { label: Published, value: published }, { label: Draft, value: draft }, { label: Rejected, value: rejected }, ]} / /Form.Item /Form /Modal / ); };:::cautionRefine 不会自动为列表中的每条记录添加EditButton/。你必须手动在Table.Column的render中放入EditButton并为其绑定onClick{() show(record.id)}编辑表单才能按记录id拉取数据。Table.ColumnIPost titleActions dataIndexactions keyactions render{(_value, record) EditButton onClick{() show(record.id)} /} /::::::caution不要忘记把记录id传给show以获取记录数据。这对edit和clone两种表单都是必需的——没有idHook 无法发起getOne查询。:::clone 模式克隆模式与编辑模式几乎一致区别在于它会以原记录数据为初始值但提交时调用create创建一个新记录——典型的复制一份场景。import React from react; import { IResourceComponentsProps } from pankod/refine-core; import { List, Table, Form, Select, Input, Modal, Space, CloneButton, useTable, useModalForm, } from pankod/refine-antd; const PostList: React.FCIResourceComponentsProps () { const { tableProps } useTableIPost(); const { modalProps: cloneModalProps, formProps: cloneFormProps, show: cloneModalShow, } useModalFormIPost({ action: clone, }); return ( List Table {...tableProps} rowKeyid Table.Column dataIndexid titleID / Table.Column dataIndextitle titleTitle / Table.Column dataIndexstatus titleStatus / Table.ColumnIPost titleActions dataIndexactions keyactions render{(_, record) ( Space CloneButton hideText sizesmall recordItemId{record.id} onClick{() cloneModalShow(record.id)} / /Space )} / /Table /List Modal {...cloneModalProps} Form {...cloneFormProps} layoutvertical Form.Item labelTitle nametitle rules{[{ required: true }]} Input / /Form.Item Form.Item labelStatus namestatus rules{[{ required: true }]} Select options{[ { label: Published, value: published }, { label: Draft, value: draft }, { label: Rejected, value: rejected }, ]} / /Form.Item /Form /Modal / ); };:::caution 与 edit 模式同理Refine 不会自动添加CloneButton/需要手动放置Table.ColumnIPost titleActions dataIndexactions keyactions render{(_value, record) CloneButton onClick{() show(record.id)} /} /同样地必须把记录 id 传给show以拉取原记录数据。:::常用属性Properties详解以下属性全部通过useModalForm({ ... })传入。此外useForm的全部 propsaction、resource、redirect、mutationMode、autoSave、onMutationSuccess等在useModalForm中同样可用完整说明见 useForm 文档。defaultFormValues仅create表单可用。设置表单的默认值用于在打开 Modal 时预填充数据。const modalForm useModalForm({ defaultFormValues: { title: Hello World, }, });在源码中它被透传给sunflower-antd的useFormSF见 packages/antd/src/hooks/form/useForm.ts并最终体现在formProps.initialValues与defaultFormValuesLoading状态上。defaultVisible默认值false设为true时Modal 初始即处于打开状态。const modalForm useModalForm({ defaultVisible: true, });源码中它以defaultVisible false作为解构默认值useModalForm.ts并通过useModal({ modalProps: { open: defaultVisible } })注入初始open状态。测试用例open initial value should be set with defaultVisible验证了defaultVisible: true时modalProps.open为true。autoSubmitClose默认值true提交成功后是否自动关闭 Modal。const modalForm useModalForm({ autoSubmitClose: false, });源码中formProps.onFinish被包装为先await onFinish(values)执行真实提交若autoSubmitClose为真则调用close()。测试用例when autoSubmitClose is true, the modal should be closed when submit is called与when autoSubmitClose is false, close should not be called when submit is called分别验证了两种取值的行为。autoResetForm默认值true提交成功后是否重置表单。const modalForm useModalForm({ autoResetForm: false, });源码中提交成功后若autoResetForm为真会调用form.resetFields()。测试用例autoResetForm is true, reset should be called when the form is submitted验证了提交后getFieldsValue()恢复为空对象。warnWhenUnsavedChanges默认值false开启后当表单存在未保存修改且尝试关闭/离开时Refine 会弹出确认框提示。该值也可以在Refine组件上全局设置。const modalForm useModalForm({ warnWhenUnsavedChanges: true, });源码层面handleClose中会检查warnWhen状态若为真则通过window.confirm询问用户确认后才关闭并清除警告状态useModalForm.ts。底层的onValuesChange来自 useForm.ts会在字段变化时调用setWarnWhen(true)。syncWithLocation源码与新版文档支持useModalForm还支持将 Modal 的开合状态与记录id同步到 URL 查询参数。默认syncWithLocation为false开启后 Modal 打开、记录 id 均反映在地址栏刷新或分享链接可直接恢复对应弹窗状态。const modalForm useModalForm({ action: edit, syncWithLocation: { key: my-modal, syncId: true }, });源码中的同步 key 默认为modal-${identifier}-${action}如modal-posts-edit可通过syncWithLocation.key自定义syncId: false可关闭 id 的同步useModalForm.ts。打开 Modal 时通过go({ query: { [key]: { open: true, id } } })写入 URL关闭时清除该参数初始化时则从useParsed().params中还原open与id。真实示例 examples/form-antd-use-modal-form/src/pages/posts/list.tsx 中对 create 与 edit 两个 Modal 均启用了syncWithLocation: true。autoResetFormWhenClose源码补充默认值true源码中还有一个文档未单列的属性关闭 Modal 时是否重置表单。handleClose中在autoResetFormWhenClose为真时会调用form.resetFields()确保下次打开时是干净的空白表单。const modalForm useModalForm({ autoResetFormWhenClose: false, // 关闭时不重置表单 });返回值Return Values逐项拆解useModalForm返回的对象由formProps、modalProps、show、close、open、submit、formLoading、id、setId、queryResult、mutationResult、form等组成。其中modalProps和formProps是使用频率最高的两个。formProps用于管理Form状态与动作的 props底层来自useForm。它包含 Ant DesignForm所需的一整套 props例如onValuesChange、initialValues、onFinish等。Form {...formProps} layoutvertical {/* Form.Item ... */} /Form其中initialValues来自query?.data?.data编辑/克隆时自动回填onFinish是包装后的提交函数提交后按autoSubmitClose/autoResetForm决定是否关闭与重置onValuesChange负责触发未保存更改警告与autoSave逻辑详见 useForm.ts。modalPropsModal组件所需的全部 props。核心字段如下。title默认值当 URL 为/posts/create时Create PostModal 标题基于 resource 与 action 自动生成。源码中通过translate(${identifier}.titles.${rest.action}, fallback)生成fallback 为getUserFriendlyName(${rest.action} ${label}, singular)因此会得到Create Post、Edit Post、Clone Post这样的文案useModalForm.ts。测试用例title should be set with action and resource验证了action: edit, resource: test时title为Edit test。okText默认值SaveModal 内提交按钮的文字通过translate(buttons.save, Save)生成。cancelText默认值CancelModal 内取消按钮的文字通过translate(buttons.cancel, Cancel)生成。width默认值1000pxModal 的宽度。源码中固定为1000px你可以在展开modalProps后覆盖Modal {...modalProps} width{720}forceRender默认值true设为true时 Modal 立即渲染而非懒渲染保证首次打开前表单实例就已挂载。okButtonProps包含提交按钮所需的全部 props如disabled、loading。当okButtonProps.onClick被调用时会触发form.submit()。你可以手动把这些 props 传给自定义按钮。源码中saveButtonPropsSF即为其实现disabled: formLoading、loading: formLoading、onClick: () form.submit()。onOk一个可以提交Modal内Form的函数适合需要手动控制提交的场景。onCancel等价于close一个可以关闭Modal的函数适合手动关闭场景。源码中它被绑定为handleClose内部会处理未保存更改警告、清除id并按autoResetFormWhenClose重置表单。visible/openvisible已标记为deprecated请改用open。当前 Modal 的可见状态默认值取决于defaultVisible。open即当前可见状态。open当前 Modal 是否打开默认取决于defaultVisible。close等价于onCancel手动关闭 Modal 的函数。适合在自定义onFinish中手动控制关闭时机const { close, modalProps, formProps, onFinish } useModalForm(); const onFinishHandler (values) { onFinish(values); close(); }; return ( Modal {...modalProps} Form {...formProps} onFinish{onFinishHandler} layoutvertical Form.Item labelTitle nametitle Input / /Form.Item /Form /Modal );submit手动提交表单的函数适合放在自定义 footer 按钮中const { modalProps, formProps, submit } useModalForm(); return ( Modal {...modalProps} footer{[ Button keysubmit typeprimary onClick{submit} Submit /Button, ]} Form {...formProps} layoutvertical Form.Item labelTitle nametitle Input / /Form.Item /Form /Modal );show打开 Modal 的函数签名是(id?: BaseKey) void。edit与clone模式下必须传入记录idHook 才会发起数据查询并回填表单const { modalProps, formProps, show } useModalForm(); return ( Button typeprimary onClick{() show()} Show Modal /Button Modal {...modalProps} Form {...formProps} layoutvertical Form.Item labelTitle nametitle Input / /Form.Item /Form /Modal / );也可以结合modalProps.onCancel实现自定义取消按钮const { modalProps, formProps } useModalForm(); return ( Modal {...modalProps} footer{ Button onClick{( e: React.MouseEventHTMLAnchorElement, MouseEvent React.MouseEventHTMLButtonElement, MouseEvent, ) modalProps.onCancel(e)} Cancel /Button } Form {...formProps} layoutvertical Form.Item labelTitle nametitle Input / /Form.Item /Form /Modal );其他返回值Key说明类型formLoading表单数据加载状态booleandefaultFormValuesLoadingdefaultFormValues的加载状态booleanformAnt Design 表单实例FormInstanceTVariablesidedit 模式下当前记录 idBaseKey|undefinedsetIdid的 setterDispatchSetStateActionBaseKey \| undefinedqueryResult单条记录查询结果QueryObserverResult{ data: TData }mutationResult表单提交触发的 mutation 结果UseMutationResult{ data: TData }, TError, { resource: string; values: TVariables }, unknown源码级原理useModalForm 内部如何工作深入 packages/antd/src/hooks/form/useModalForm/useModalForm.ts 可以看到几个关键设计点默认值集中解构defaultVisible false、autoSubmitClose true、autoResetForm true、autoResetFormWhenClose true在函数入口统一解构并赋默认值与文档中的默认值完全一致。组合而非重写useModalForm内部调用useForm复用其全部能力数据获取、提交、redirect、notification、服务端校验错误映射等再叠加useModal管理开关返回值通过OmitUseFormReturnType, saveButtonProps | deleteButtonProps剔除表格场景专用的按钮 props 后与 Modal 状态合并。show的守卫逻辑handleShowaction edit || action clone时要求必须存在id传入的showId或已有id否则不打开 Modalcreate模式则无需 id 直接打开。这从机制上保证了编辑/克隆必须先有记录 id。close的清理逻辑handleClose关闭前先处理warnWhenUnsavedChanges确认框确认后清除id、关闭 Modal并在autoResetFormWhenClose为真时form.resetFields()若启用autoSave.invalidateOnClose且保存成功还会触发相关资源失效invalidate list/many/detail。提交管线onFinish 包装formProps.onFinish被包装为先提交、再按需关闭、再按需重置okButtonProps的onClick则直接form.submit()保证点确定按钮走与表单提交一致的路径。URL 同步syncWithLocation开启时通过go({ query: ... })把{ open, id }写入查询参数key 默认modal-${identifier}-${action}初始化时从useParsed().params恢复状态。测试用例should \meta[syncWithLocationKey] overrided by default验证了该模式下getOne请求会携带modal-posts-edit 这一 meta 键。测试用例验证行为契约packages/antd/src/hooks/form/useModalForm/index.spec.tsx 对useModalForm的行为做了系统验证是理解其契约的最佳文档初始open为falsedefaultVisible: true时初始open为true调用onCancel后open变为false调用show后open变为trueshow(id)会同步更新内部idtitle由action与resource组合生成Edit testautoSubmitClose: true时提交成功后 Modal 自动关闭为false时提交后仍保持打开autoResetForm: true时提交后表单字段被重置无论mutationMode为pessimistic、optimistic还是undoable提交成功mutation 成功后 Modal 都会关闭。这些用例与文档中的属性语义一一对应可作为你自定义行为时的回归基准。一个完整的可运行示例仓库中的 examples/form-antd-use-modal-form 是一个完整的可运行项目其 src/pages/posts/list.tsx 展示了 create、edit、show 三个 Modal 共存的真实写法create 与 edit 使用useModalForm均开启syncWithLocation: trueformLoading配合Spin显示加载态show 使用useShow手动管理。其关键片段export const PostList () { const { tableProps } useTableIPost(); // Create Modal const { modalProps: createModalProps, formProps: createFormProps, show: createModalShow, formLoading: createFormLoading, } useModalFormIPost({ action: create, syncWithLocation: true, }); // Edit Modal const { modalProps: editModalProps, formProps: editFormProps, show: editModalShow, formLoading: editFormLoading, } useModalFormIPost({ action: edit, syncWithLocation: true, }); return ( List createButtonProps{{ onClick: () createModalShow(), }} Table {...tableProps} rowKeyid Table.Column dataIndexid titleID / Table.Column dataIndextitle titleTitle / Table.ColumnIPost titleActions dataIndexactions keyactions render{(_, record) ( Space EditButton hideText sizesmall recordItemId{record.id} onClick{() editModalShow(record.id)} / DeleteButton hideText sizesmall recordItemId{record.id} / /Space )} / /Table /List Modal {...createModalProps} Spin spinning{createFormLoading} Form {...createFormProps} layoutvertical {/* Form.Itemtitle / status */} /Form /Spin /Modal Modal {...editModalProps} Spin spinning{editFormLoading} Form {...editFormProps} layoutvertical {/* Form.Itemtitle / status */} /Form /Spin /Modal / ); };启动方式在仓库根目录安装依赖后进入examples/form-antd-use-modal-form目录按项目 README 的指引运行即可。它依赖仓库内的数据提供器与假数据服务可直接体验完整的 CRUD 弹窗流程。API 参考速查表Properties属性useModalForm继承useForm的全部 props。带*的属性在RefineContext中有默认值也可在Refine组件上设置useModalForm的局部值优先带**的属性默认值随action变化——action为create时redirect默认到edit新记录的编辑页action为edit时redirect默认到list。属性说明默认值action表单动作show|edit|create|clone-defaultFormValues表单默认值仅 create 可用-defaultVisibleModal 默认是否可见falseautoSubmitClose提交成功后自动关闭 ModaltrueautoResetForm提交成功后重置表单trueautoResetFormWhenClose关闭 Modal 时重置表单truewarnWhenUnsavedChanges有未保存修改时离开弹窗/页面给出确认提示falsesyncWithLocation将开关状态与记录 id 同步到 URLfalsemutationModepessimistic|optimistic|undoable*redirect提交成功后的跳转目标**resource资源名当前路由资源dataProviderName数据提供器名称*Type Parameters类型参数属性说明默认值TData查询结果数据类型继承BaseRecordBaseRecordTError自定义错误类型继承HttpErrorHttpErrorTVariables提交参数类型{}掌握了useModalForm的三种 action、五个核心属性与完整的返回值语义再加上源码层面的实现佐证你就可以在 Refine Ant Design 项目中快速落地弹窗式 CRUD并针对自动关闭、自动重置、未保存提醒、URL 同步等细节进行精确控制。若需要抽屉式表单可进一步阅读同族的useDrawerForm与useStepsForm。【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考