
Refine 3.x 的 Ant Design useForm 完全指南从路由驱动到提交、重定向与自动保存【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refineRefine 的pankod/refine-antd3.x 版本对应包名中导出的useForm钩子是构建管理后台 CRUD 表单的核心入口它把 Refine 的数据层dataProvider、路由上下文与 Ant Design 的Form组件桥接在一起自动处理「创建 / 编辑 / 克隆」三种动作、数据回填、提交后的重定向与缓存失效。读完本文你将掌握useForm的全部配置属性、返回值并能在实际项目中基于源码原理写出可维护的表单页面。本指南以仓库中的官方文档 version-3.xx.xx/api-reference/antd/hooks/form/useForm.md 为主体辅以当前仓库的 packages/antd/src/hooks/form/useForm.ts 与 packages/core/src/hooks/form/index.ts 源码进行纵深讲解并提供可直接参考的完整示例 examples/form-antd-use-form。什么是 useFormuseForm用于管理表单。它返回控制 Ant Design Form 开发。从源码看这一分层非常清晰packages/antd/src/hooks/form/useForm.ts 中antd 版本的useForm内部创建了 Ant Design 的Form.useForm()实例并调用useFormCore即 core 包导出的useForm再把useFormCoreResult与formProps、saveButtonProps等 UI 相关返回值合并后对外暴露packages/core/src/hooks/form/index.ts 中core 版本的useForm负责编排 Refine 的数据钩子内部通过useResourceParams从路由推断id、resource与action并通过useOne、useCreate、useUpdate完成数据读取与变更。换句话说antd 的useForm core 的useForm数据编排 Ant DesignForm实例UI 状态两者职责分离、互不耦合。基本用法一个编辑表单官方文档用「添加编辑表单」演示useForm的最基本用法。以下代码位于示例项目的 examples/form-antd-use-form/src/pages/posts/edit.tsx为便于阅读做了少量精简但保留了queryResult的典型取数方式import { Edit, useForm } from pankod/refine-antd; import { Form, Input, Select } from antd; interface IPost { id: number; title: string; status: published | draft | rejected; } export const PostEdit: React.FC () { const { formProps, saveButtonProps, query: queryResult } useFormIPost(); const postData queryResult?.data?.data; return ( Edit saveButtonProps{saveButtonProps} Form {...formProps} layoutvertical Form.Item labelTitle nametitle Input / /Form.Item Form.Item labelStatus namestatus Select options{[ { label: Published, value: published }, { label: Draft, value: draft }, { label: Rejected, value: rejected }, ]} / /Form.Item /Form /Edit ); };关键点拆解formProps包含管理 Ant DesignForm组件状态与动作所需的全部值。它把onFinish、onKeyUp、onValuesChange、initialValues等属性一并打包见 packages/antd/src/hooks/form/useForm.ts直接展开到Form上即可。saveButtonProps提供给「提交」按钮的全部必要 propsdisabled、onClick等并会自动更新加载状态。在 antd 实现中它本质上是一个{ disabled: formLoading, onClick: () form.submit() }的对象见 packages/antd/src/hooks/form/useForm.ts。路由驱动如果访问/posts/edit/1234useForm会自动识别出「编辑」上下文通过useOne拉取 id 为1234的 post 数据并回填表单之后即可修改并提交。关于如何判定编辑上下文见下文 action 属性 一节。泛型参数useFormIPost()中IPost表示要编辑的记录类型同时它也是 mutation变更响应的默认类型。完整的泛型列表见 Type Parameters 一节。提示如果需要在 Modal 或 Drawer 中展示表单此时路由参数可能不存在请使用同目录下的useModalForm或useDrawerForm钩子。Properties配置属性actionuseForm可以处理edit、create和clone三种动作。默认情况下它会从路由推断action路由为/posts/create→action: create路由为/posts/edit/1→action: edit路由为/posts/clone/1→action: clone此时展示表单使用资源resource的create组件。如果无法从路由判定动作例如在 modal 中使用表单或使用自定义路由可以通过显式传入action属性来覆盖。从 core 源码看useForm通过useResourceParams({ resource, id, action })完成这一推断并在内部用isEdit、isClone、isCreate三个布尔值区分分支逻辑见 packages/core/src/hooks/form/index.ts。create创建action: create用于创建一条先前不存在的新记录。useForm在 create 模式下底层使用useCreate。import { Create, Form, Input, useForm } from pankod/refine-antd; interface IPost { id: number; title: string; content: string; } const PostCreatePage: React.FC () { const { formProps, saveButtonProps } useFormIPost(); return ( Create saveButtonProps{saveButtonProps} Form {...formProps} layoutvertical Form.Item labelTitle nametitle rules{[{ required: true }]} Input / /Form.Item Form.Item labelContent namecontent rules{[{ required: true }]} Input.TextArea / /Form.Item /Form /Create ); };edit编辑action: edit用于编辑已有记录需要id来确定要编辑的记录默认从路由读取id也可以通过setId函数或id属性修改。它根据id用useOne拉取记录数据并返回queryResult供你填充表单表单提交后用useUpdate更新记录。示例路由为/posts/edit/123import { Edit, Form, Input, useForm } from pankod/refine-antd; const PostEditPage: React.FC () { const { formProps, saveButtonProps } useFormIPost(); return ( Edit saveButtonProps{saveButtonProps} Form {...formProps} layoutvertical {/* 与 create 相同的 Form.Item 结构 */} /Form /Edit ); };clone克隆action: clone用于克隆已有记录同样需要id默认从路由读取可用setId修改。你可以把 clone 理解为「另存为」它与 edit 类似但提交时是创建新记录而非更新现有记录。它根据id用useOne拉取数据填充表单提交后通过useCreate创建新记录import { Create, Form, Input, useForm } from pankod/refine-antd; // 路由 /posts/clone/123 const PostClonePage: React.FC () { const { formProps, saveButtonProps } useFormIPost(); // 注意clone 使用 Create 组件展示 return ( Create saveButtonProps{saveButtonProps} Form {...formProps} layoutvertical {/* 表单字段与 create/edit 相同 */} /Form /Create ); };resource默认值从当前 URL 读取resource值。resource会被作为参数传给dataProvider的方法通常用于作为 API 端点路径具体如何处理取决于你dataProvider的实现参见 创建 data provider。action为create时传给dataProvider的create方法action为edit时传给update与getOne方法action为clone时传给create与getOne方法。useForm({ resource: categories, });从 core 源码看useResourceParams会把资源信息解析为resource名称与identifier唯一标识并在后续useOne/useCreate/useUpdate中以identifier ?? resource.name作为最终传给 dataProvider 的 resource见 packages/core/src/hooks/form/index.ts 与 packages/core/src/hooks/form/index.ts。idid用于确定要edit或clone的记录默认从路由读取可用setId函数或id属性修改。当你需要从另一个页面编辑或克隆某个resource时特别有用。注意当action: edit或action: clone时id是必需的。useForm({ action: edit, // 或 clone resource: categories, id: 1, // 请求路径为 BASE_URL_FROM_DATA_PROVIDER/categories/1 });core 源码对这一点做了额外保护当通过 props 传入自定义resource时id不再从 URL 推断以避免错误的请求此时必须在 props 中显式传入id否则开发模式下会输出警告见 packages/core/src/hooks/form/index.ts 与idWarningMessage的定义 packages/core/src/hooks/form/index.ts。redirectredirect用于确定表单提交成功后跳转到的页面默认跳转到list。可以设置为show | edit | list | create或falsefalse表示提交后不跳转到列表页useForm({ redirect: false, });core 实现中redirect既是一个配置项也是一个可编程调用的函数内部通过useRedirectionAfterSubmission完成实际跳转并在redirectPage中合并 props 与Refine全局默认值见 packages/core/src/hooks/form/index.ts。onMutationSuccessmutation提交成功后会调用的回调函数接收以下参数data根据action的不同为useCreate或useUpdate的返回值variables传给 mutation 的变量contextreact-query 上下文。useForm({ onMutationSuccess: (data, variables, context) { console.log({ data, variables, context }); }, });onMutationErrormutation 失败后会调用的回调函数参数与onMutationSuccess相同useForm({ onMutationError: (data, variables, context) { console.log({ data, variables, context }); }, });这里有一个值得注意的 antd 层实现细节antd 版本的useForm对onMutationError做了包装见 packages/antd/src/hooks/form/useForm.ts——当服务端返回字段级错误如error.errors { title: Title is required }时它会自动把这些错误解析成 Ant Design 表单可识别的{ name, errors }结构并通过form.setFields回填到对应字段上从而实现「服务端表单校验」。若想关闭这一默认行为可以设置disableServerSideValidation: true。invalidates用于管理 mutation 结束时发生的缓存失效invalidation。默认情况下会根据当前resource失效以下查询create或clone模式下list和manyedit模式下list、many和detail。useForm({ invalidates: [list, many, detail], });在 core 源码中invalidates会被透传到useUpdate/useCreate的调用参数中当autoSave触发时则强制为空数组见 packages/core/src/hooks/form/index.ts。dataProviderName如果有多个dataProvider应该指定要使用的dataProviderName。当你希望对某个特定资源使用不同的dataProvider时非常有用useForm({ dataProviderName: second-data-provider, });提示如果希望所有资源页面都使用不同的dataProvider可以在Refine组件上使用dataProvider属性进行全局配置。mutationModemutation mode 决定 mutation 以哪种模式运行pessimistic、optimistic和undoable默认是pessimistic。每种模式对应不同的用户体验useForm({ mutationMode: undoable, // pessimistic | optimistic | undoable });core 源码中mutationMode默认值来自useMutationMode()即Refine的全局配置并且pessimistic模式与非悲观模式在onFinish的 Promise 解析时机上有明显差异悲观模式下需要等 mutation 真正成功才 resolve 并执行重定向非悲观模式则立即 resolve、延迟执行重定向见 packages/core/src/hooks/form/index.ts 与 packages/core/src/hooks/form/index.ts。详细说明参见 Mutation Mode。successNotification该属性需要NotificationProvider才能生效。表单提交成功后useForm会调用NotificationProvider的open函数显示成功通知。通过此属性可以自定义成功通知useForm({ successNotification: (data, values, resource) { return { message: Post Successfully created with ${data.title}, description: Success with no errors, type: success, }; }, });errorNotification该属性需要NotificationProvider才能生效。表单提交失败后useForm会调用open函数显示错误通知。通过此属性可以自定义错误通知useForm({ action: create, resource: post, errorNotification: (data, values, resource) { return { message: Something went wrong when deleting ${data.id}, description: Error, type: error, }; }, });未自定义时的默认值为{ message: Error when updating resource-name (status code: ${err.statusCode}) 或 Error when creating resource-name (status code: ${err.statusCode}), description: Error, type: error }metaDatametaData用于两个目的向 dataProvider 方法传递附加信息使用纯 JavaScript 对象JSON生成 GraphQL 查询参见 GraphQL data provider 的编辑页部分。下面的例子把metaData对象中的headers属性传给create方法。用类似的逻辑你可以传任意属性来专门处理 dataProvider 方法useForm({ metaData: { headers: { x-meta-data: true }, }, }); const myDataProvider { //... create: async ({ resource, variables, metaData }) { const headers metaData?.headers ?? {}; const url ${apiUrl}/${resource}; const { data } await httpClient.post(url, variables, { headers }); return { data, }; }, //... };说明在 3.x 文档中该属性名为metaData在当前仓库的较新源码中对应能力已被拆分为meta通用元数据、queryMeta仅用于useOne查询与mutationMeta仅用于变更见 packages/core/src/hooks/form/types.ts。queryOptions仅在action: edit或action: clone模式下生效。在 edit 或 clone 模式下Refine 使用useOne钩子拉取数据。可以通过queryOptions传入 react-query 的 useQuery 选项useForm({ queryOptions: { retry: 3, }, });core 源码展示了它如何使用queryOptions会被合并进useOne的查询配置并强制附加enabled: !isCreate id ! undefined条件见 packages/core/src/hooks/form/index.ts。createMutationOptions仅在action: create或action: clone时可用。在 create 或 clone 模式下Refine 使用useCreate创建数据。可以通过createMutationOptions传入 react-query 的 mutation 选项useForm({ createMutationOptions: { retry: 3, }, });updateMutationOptions仅在action: edit时可用。在 edit 模式下Refine 使用useUpdate更新数据。可以通过updateMutationOptions传入 react-query 的 mutation 选项useForm({ updateMutationOptions: { retry: 3, }, });warnWhenUnsavedChanges默认值false设为true时当用户带着未保存的更改离开页面会显示警告可防止用户意外离开页面useForm({ warnWhenUnsavedChanges: true, });该选项也可以在Refine config中全局设置。其实现依赖于onValuesChangeantd 层在onValuesChange里通过useWarnAboutChange提供的setWarnWhen(true)标记「有未保存更改」见 packages/antd/src/hooks/form/useForm.ts。submitOnEnter默认值false设为true时按 Enter 键会提交表单useForm({ submitOnEnter: true, });antd 层通过onKeyUp实现当submitOnEnter为 true 且按键为 Enter 时调用form.submit()见 packages/antd/src/hooks/form/useForm.ts。liveMode是否在收到相关 live 事件时自动auto或手动manual更新数据。可用于在整个应用中实时更新和展示数据。关于 live mode 的更多信息参见 Live / Realtime。useForm({ liveMode: auto, });onLiveEvent当订阅产生新事件时执行的回调函数useForm({ onLiveEvent: (event) { console.log(event); }, });liveParams传递给 liveProvider 的subscribe方法的参数。Return Values返回值提示core 的useForm的所有返回值在 antd 的useForm中同样可用。formForm实例详见 Ant Design Form 实例 API。formProps管理Form状态与动作所必需的对象包含以下关键字段见 packages/antd/src/hooks/form/useForm.tsonFinish表单提交时调用会根据action属性调用对应的 mutation。它与formProps.onFinish的唯一区别是传给formProps.onFinish的值是必须的而独立的onFinish可以自动从表单状态获取值。onValuesChangewarnWhenUnsavedChanges功能依赖此函数工作。如果你要覆盖表单的onValuesChange请记住这一点此外它还承载了autoSave的触发逻辑。onKeyUp按键时调用的函数默认在按 Enter 键时调用form.submit()配合submitOnEnter。initialValues当action为edit或clone时initialValues会被设为useOne返回的data。saveButtonProps包含表单内「提交」按钮所需的所有 propsdisabled、loading等。当saveButtonProps.onClick被调用时会触发form.submit()。你可以手动把这些 props 传给自定义按钮const { saveButtonProps } useForm(); // Button {...saveButtonProps}Submit/ButtonformLoading表单的加载状态。当useForm正在提交、或 edit/clone 模式正在拉取数据时它为true。core 层的定义为isMutationLoading || queryResult.query.isFetching见 packages/core/src/hooks/form/index.ts。queryResult当action为edit或clone或提供了带id的resource时useForm会调用useOne并把返回值设置为queryResult属性const { queryResult } useForm(); const { data } queryResult;在较新源码中该返回值对应query见 packages/core/src/hooks/form/types.ts但 3.x 文档中两者所指相同queryResult.data.data即当前记录。mutationResult在create或clone模式下useForm调用useCreate在edit模式下调用useUpdate并把结果设置为mutationResult属性const { mutationResult } useForm(); const { data } mutationResult;在较新源码中该返回值对应mutation见 packages/core/src/hooks/form/types.ts。setIduseForm从路由推断id。如果你想动态修改id可以使用setId函数const { id, setId } useForm(); const handleIdChange (id: string) { setId(id); }; return ( div input value{id} onChange{(e) handleIdChange(e.target.value)} / /div );redirect函数默认情况下mutation 成功后useForm会重定向到list页面。要重定向到其他页面可以使用redirect函数以编程方式指定目的地或在钩子选项中设置redirect属性。下面的例子在 mutation 成功后重定向到show页面const { onFinish, redirect } useForm(); // -- const handleSubmit async (e: React.FormEventHTMLFormElement) { e.preventDefault(); const data await onFinish(formValues); redirect(show, data?.data?.id); }; // --onFinish函数onFinish是表单提交时调用的函数会根据action属性调用对应的 mutation。你可以通过在钩子选项中传入自定义onFinish覆盖默认行为例如在发送给 API 之前修改表单数据见下文的 FAQ。在 antd 实现中onFinish(values)会在未传值时自动取form.getFieldsValue(true)即从表单状态自动获取全部字段值见 packages/antd/src/hooks/form/useForm.ts。FAQ如何失效invalidate其他资源可以借助useInvalidate钩子来失效其他资源。当你需要失效与当前资源无关的其他资源时非常有用import React from react; import { Create, Form, Input, useForm } from pankod/refine-antd; const PostEdit () { const invalidate useInvalidate(); useForm({ onMutationSuccess: (data, variables, context) { invalidate({ resource: users, invalidates: [resourceAll], }); }, }); // --- };如何在提交给 API 之前修改表单数据你可能需要在数据发送到 API 之前对其进行修改。例如把用户在两个独立输入框name和surname中填写的值作为fullName发给 APIimport React from react; import { Create, Form, Input, useForm } from pankod/refine-antd; export const UserCreate: React.FC () { const { formProps, saveButtonProps, onFinish } useForm(); const handleOnFinish (values) { onFinish({ fullName: ${values.name} ${values.surname}, }); }; return ( Create saveButtonProps{saveButtonProps} Form {...formProps} onFinish{handleOnFinish} layoutvertical Form.Item labelName namename Input / /Form.Item Form.Item labelSurname namesurname Input / /Form.Item /Form /Create ); };深入服务端校验错误如何自动映射到表单字段antd 版本的useForm在包装onMutationError时会自动完成「服务端错误 → Ant Design 表单字段错误」的映射见 packages/antd/src/hooks/form/useForm.ts先通过flattenObjectKeys与propertyPathToArray把表单当前值展开为字段名路径数组用form.setFields清除旧错误遍历error.errors中每个字段的错误数组直接使用、字符串包装成单元素数组、布尔true转为Field is not valid.、含key的对象则用useTranslate做 i18n 翻译最后统一form.setFields([...parsedErrors])回填到表单。这意味着只要 dataProvider 返回形如{ message, errors: { title: [Title is required] } }的HttpError前端表单就能开箱即用地展示字段级校验错误配合Refine的options.disableServerSideValidation可全局关闭此行为。API ReferenceProperties以下属性中的*标记项在RefineContext中有默认值也可以在Refine组件上设置useForm会优先使用Refine传入的默认值但局部传入的值会覆盖它属性类型默认值说明actioncreate \| edit \| clone从路由推断表单模式resourcestring从当前 URL 读取传给 dataProvider 的资源名idBaseKey从路由读取要 edit/clone 的记录 idredirectshow \| edit \| list \| create \| falselist提交成功后的跳转目标mutationModepessimistic \| optimistic \| undoablepessimistic*mutation 执行模式undoableTimeoutnumber5000*undoable 模式下执行前等待的毫秒数invalidatesArraykeyof IQueryKeys[list, many, detail]mutation 结束时的缓存失效范围dataProviderNamestring—多 dataProvider 时指定使用哪个metaDataMetaQuery—传给 dataProvider 的附加信息新版拆分为meta/queryMeta/mutationMetaonMutationSuccess(data, variables, context) void—mutation 成功回调onMutationError(error, variables, context) void—mutation 失败回调successNotification(data, values, resource) OpenParams—自定义成功通知需 NotificationProvidererrorNotification(data, values, resource) OpenParams—自定义错误通知需 NotificationProviderqueryOptionsUseQueryOptions—仅 edit/clone 模式透传给useOnecreateMutationOptionsUseMutationOptions—仅 create/clone 模式透传给useCreateupdateMutationOptionsUseMutationOptions—仅 edit 模式透传给useUpdatewarnWhenUnsavedChangesbooleanfalse离开页面时提示未保存更改submitOnEnterbooleanfalse按 Enter 提交表单liveModeauto \| manual—实时数据更新模式onLiveEvent(event) void—订阅新事件回调liveParamsobject—传给 liveProvider.subscribe 的参数disableServerSideValidationbooleanfalse关闭服务端校验错误自动映射到表单字段autoSaveAutoSaveProps—自动保存配置enabled、debounce、onFinish等Return values属性说明类型onFinish触发 mutation(values?: TVariables) PromiseCreateResponseTData \| UpdateResponseTData \| voidformAnt Design 表单实例FormInstanceformPropsAnt Design 表单 propsFormPropssaveButtonProps提交按钮的 props{ disabled: boolean; onClick: () void; loading?: boolean; }redirect自定义重定向函数(redirect: list \| edit \| show \| create \| false, idFromFunction?: BaseKey \| undefined) dataqueryResult记录查询的结果edit/clone 模式QueryObserverResultTmutationResult提交触发的 mutation 的结果UseMutationResultTformLoading表单请求的加载状态booleanidclone和create动作的记录 idBaseKeysetIdid的 setterDispatchSetStateActionstring \| number \| undefinedType Parameters参数说明默认值TData查询结果数据继承BaseRecordBaseRecordTError自定义错误对象继承HttpErrorHttpErrorTVariables提交的参数值{}从 antd 源码的类型定义看完整泛型还包括TQueryFnData查询函数原始返回、TResponsemutation 的返回数据默认等于TData与TResponseErrormutation 的错误类型默认等于TError日常使用只需关注TData、TError、TVariables三个即可见 packages/antd/src/hooks/form/useForm.ts。完整示例官方为useForm提供了可直接运行的 CodeSandbox 示例form-antd-use-form对应仓库中的 examples/form-antd-use-form其中包含完整的create、edit、list页面实现见 examples/form-antd-use-form/src/pages/posts/create.tsx、examples/form-antd-use-form/src/pages/posts/edit.tsx、examples/form-antd-use-form/src/pages/posts/list.tsx是快速上手和二次开发的最佳参照。相关端到端测试见 cypress/e2e/form-antd-use-form。相关链接core 版 useForm 文档useModalFormModal 中表单useDrawerFormDrawer 中表单useStepsForm分步表单Mutation Mode 详解NotificationProviderdataProvider 文档【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考