Refine v5 中的 useStepsForm:用 React Hook Form 构建多步骤表单的完整实战指南

发布时间:2026/11/11 14:40:28

Refine v5 中的 useStepsForm:用 React Hook Form 构建多步骤表单的完整实战指南 Refine v5 中的 useStepsForm用 React Hook Form 构建多步骤表单的完整实战指南【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine导读useStepsForm是 Refine 在refinedev/react-hook-form包中提供的高阶表单 Hook用于管理多步骤分步表单它替你维护当前处于哪一步的状态、提供跳转到指定步骤的方法并在步骤切换时自动触发校验。本文以仓库中 form-react-hook-form-use-steps-form 示例为骨架从 Hook 用法、完整可运行代码、配置项、返回值一直深入到 packages/react-hook-form/src/useStepsForm/index.ts 的源码实现与测试用例读完你可以在 Refine v5 React Hook Form 的 headless 项目中独立实现带校验、带回显的多步骤创建/编辑表单。一、示例速览一个三步的 Post 表单仓库中的示例文档 documentation/docs/examples/form/react-hook-form/useStepsForm.md 明确说明了本示例的核心能力useStepsFormallows you to manage a form with multiple steps. It provides features such as which step is currently active, the ability to go to a specific step and validation when changing steps etc.即跟踪当前激活步骤、可跳转到任意指定步骤、步骤切换时进行校验三大能力。示例把一篇 Post 的创建/编辑拆成了三个步骤步骤索引步骤名表单字段0Titletitle必填1Statusstatuspublished / draft / rejected2Category and contentcategory.id必填、content必填示例的依赖与运行方式见 examples/form-react-hook-form-use-steps-form/package.json核心依赖为refinedev/core^5.0.12、refinedev/react-hook-form^5.0.4、react-hook-form^7.57.0、refinedev/react-router与react-router数据源使用refinedev/simple-rest指向https://api.fake-rest.refine.dev。示例在本地运行时可直接在仓库根目录执行pnpm install pnpm --filter form-react-hook-form-use-steps-form dev二、在页面中接入 useStepsForm2.1 引入并解构 HookuseStepsForm是泛型 Hook类型参数依次是查询数据类型、错误类型和变量类型用于在编写时获得完整的类型检查。参考 examples/form-react-hook-form-use-steps-form/src/pages/posts/create.tsximport { useSelect, type HttpError } from refinedev/core; import { useStepsForm } from refinedev/react-hook-form; import { Controller } from react-hook-form; import type { IPost } from ../../interfaces; const stepTitles [Title, Status, Category and content]; export const PostCreate: React.FC () { const { refineCore: { onFinish, formLoading }, register, handleSubmit, formState: { errors }, steps: { currentStep, gotoStep }, control, } useStepsFormIPost, HttpError, IPost(); // ... };关键返回值的分工steps.currentStep当前步骤索引从0开始计数steps.gotoStep(step)程序化跳转到指定步骤refineCore.onFinish提交数据到 data provider 的处理器直接交给handleSubmit使用register、handleSubmit、formState.errors、controlReact Hook Form 的原生能力useStepsForm全量透传。IPost接口定义在 examples/form-react-hook-form-use-steps-form/src/interfaces/index.d.ts其中category是嵌套的关系型字段{ id: number }为后续演示useSelect与Controller提供了基础。2.2 按 currentStep 条件渲染表单字段拿到currentStep后用switch语句按步骤索引渲染对应的字段组。示例中把这一逻辑封装为renderFormByStep(step)函数const renderFormByStep (step: number) { switch (step) { case 0: return ( labelTitle: /label input idtitle {...register(title, { required: This field is required, })} / {errors.title span{errors.title.message}/span} / ); case 1: return ( labelStatus: /label select idstatus {...register(status)} option valuepublishedpublished/option option valuedraftdraft/option option valuerejectedrejected/option /select / ); case 2: return ( Controller namecategory.id control{control} render{({ field }) { return ( labelCategory: /label select idcategory {...field} {options?.map((category) ( option key{category.value} value{category.value} {category.label} /option ))} /select {errors.category span{errors.category.message}/span} / ); }} / br / br / labelContent: /label textarea idcontent {...register(content, { required: This field is required, })} rows{10} cols{50} / {errors.content span{errors.content.message}/span} / ); } };这里体现了两种受控方式的混用普通字段title、status、content直接用register注册并内联声明校验规则如required: This field is required嵌套关系字段category.id因为是父对象下的子属性示例使用Controllercontrol接管渲染同时从useSelect拉取分类选项options中的value/label。2.3 步骤导航栏与提交按钮在form外部渲染一组步骤按钮让用户可以直接跳转到任意步骤并用背景色高亮当前步骤if (formLoading) { return divLoading.../div; } return ( div style{{ display: flex, flexDirection: column, gap: 16 }} div style{{ display: flex, gap: 36 }} {stepTitles.map((title, index) ( button key{index} onClick{() gotoStep(index)} style{{ backgroundColor: currentStep index ? lightgray : initial, }} {index 1} - {title} /button ))} /div form autoCompleteoff{renderFormByStep(currentStep)}/form div style{{ display: flex, gap: 8 }} {currentStep 0 ( button onClick{() { gotoStep(currentStep - 1); }} Previous /button )} {currentStep stepTitles.length - 1 ( button onClick{() { gotoStep(currentStep 1); }} Next /button )} {currentStep stepTitles.length - 1 ( button onClick{handleSubmit(onFinish)}Save/button )} /div /div );导航逻辑的要点顶部按钮直接gotoStep(index)跳转到任意步骤Previous / Next基于currentStep做 ±1 跳转且只在条件满足时渲染Save只在最后一步出现调用handleSubmit(onFinish)——注意onFinish来自refineCore它会把校验通过后的表单数据交给 Refine 的 data provider 执行create变更。2.4 编辑页的差异数据回显编辑场景与创建场景几乎一致唯一关键差异是回显。参考 examples/form-react-hook-form-use-steps-form/src/pages/posts/edit.tsxconst { refineCore: { onFinish, formLoading, query }, register, handleSubmit, formState: { errors }, steps: { currentStep, gotoStep }, control, } useStepsFormIPost, HttpError, IPost(); const { options } useSelect({ resource: categories, defaultValue: query?.data?.data.category.id, pagination: { mode: server, }, });refineCore.query是 Refine 内部为edit动作发起的详情查询query?.data?.data即当前 Post 数据useSelect通过defaultValue: query?.data?.data.category.id预选当前分类分类下拉通过Controller的field加上value{query?.data?.data.category.id}显式回显普通字段title、status、content不需要手动初始化useStepsForm内部会自动把查询数据同步进 React Hook Form 的字段值见下文源码分析。列表页与路由装配分别在 examples/form-react-hook-form-use-steps-form/src/pages/posts/list.tsx 与 examples/form-react-hook-form-use-steps-form/src/App.tsxApp.tsx中注册了posts资源的list、create、edit三个路由并开启了syncWithLocation与warnWhenUnsavedChanges。三、配置项stepsProps 与 autoSaveuseStepsForm的完整配置说明位于 documentation/docs/packages/react-hook-form/use-steps-form/index.md。它继承refinedev/react-hook-form中useForm的全部能力因此refineCoreProps、autoSave等配置同样可用。3.1 stepsProps.defaultStep设置表单初始激活的步骤计数从0开始默认值为0const stepsForm useStepsForm({ stepsProps: { defaultStep: 0, }, });3.2 stepsProps.isBackValidate控制向后切换步骤时是否校验当前步骤字段默认false。为true时用户点击 Previous 也会先校验当前步骤的字段校验失败则不允许返回const stepsForm useStepsForm({ stepsProps: { isBackValidate: true, }, });源码中的默认值在 packages/react-hook-form/src/useStepsForm/index.tsconst { defaultStep 0, isBackValidate false } stepsProps ?? {};3.3 autoSave继承自 useForm编辑场景下可以开启自动保存。配置示例useStepsForm({ refineCoreProps: { autoSave: { enabled: true, // 默认 false debounce: 2000, // 默认 1000ms onFinish: (values) ({ foo: bar, ...values }), // 提交前改写数据 invalidateOnUnmount: true, // 卸载时失效 list/many/detail 查询 }, }, });按官方文档说明autoSave仅在 edit 模式下生效编辑时改动字段会按debounce延迟自动提交创建模式下仍需手动保存。onMutationSuccess/onMutationError回调也可用于在自动保存成功或失败后做额外处理。四、源码级原理useStepsForm 内部到底做了什么多步骤状态的实现非常精简全部逻辑集中在 packages/react-hook-form/src/useStepsForm/index.ts。4.1 内部状态与步骤跳转const [current, setCurrent] useState(defaultStep); const go (step: number) { let targetStep step; if (step 0) { targetStep 0; } setCurrent(targetStep); }; const gotoStep async (step: number) { if (step current) { return; } if (step current !isBackValidate) { go(step); return; } const isValid await trigger(); if (isValid) { go(step); } };可以提炼出三条可验证的跳转规则目标步骤与当前相同时直接return避免无意义的重复校验与重渲染向后跳转step current且isBackValidate为false时直接切换不做校验其余情况向前跳转或向后但开启了isBackValidate都会先调用 React Hook Form 的trigger()全量校验校验通过才真正切步——这就是切换步骤时自动校验的实现来源。另外go内部对负数步骤做了钳制gotoStep(-7)会落在第0步。4.2 编辑数据的自动回填useStepsForm通过一个useEffect在查询数据到达时把详情回填进表单useEffect(() { const data query?.data?.data; if (!data) return; const registeredFields Object.keys(getValues()); Object.entries(data).forEach(([key, value]) { const name key as PathTVariables; if (registeredFields.includes(name)) { if (!get(dirtyFields, name)) { setValue(name, value); } } }); }, [query?.data, current, setValue, getValues]);这段代码有两个值得注意的设计只回填已经注册的字段registeredFields.includes(name)避免把无关数据塞进表单状态通过dirtyFields判断用户是否已修改过该字段——用户改过的字段不会被覆盖只有未被触碰的字段才用服务器数据setValue。这正是编辑页title、content等字段无需手动初始化的原因。4.3 返回值结构useStepsForm返回useForm的全部返回值refineCore、register、handleSubmit、formState、control、trigger等并在其上追加steps命名空间steps: { currentStep: number; // 当前步骤从 0 计数 gotoStep: (step: number) void; // 程序化切换步骤 }4.4 测试用例印证packages/react-hook-form/src/useStepsForm/index.spec.ts 中的测试与上述实现一一对应defaultStep: 4时steps.currentStep初始即为4调用gotoStep(1)后currentStep变为1gotoStep(-7)时currentStep被钳制为0当defaultStep为2时调用gotoStep(2)currentStep保持2不变关于回填行为测试分别模拟了空dirtyFields与{ field2: true }两种场景断言setValue的调用次数等于数据字段总数 − 已脏字段数且只对未脏字段以(字段名, 值)形式调用——精确验证了用户已编辑字段不被覆盖的逻辑。五、类型参数速查useStepsForm的七个泛型参数均来自官方文档的 Type Parameters 表默认值列已注明泛型参数含义默认值TQueryFnData查询函数返回的数据类型需继承BaseRecordBaseRecordTError自定义错误对象需继承HttpErrorHttpErrorTVariables变更mutation函数使用的字段值类型{}TContextReact Hook FormuseForm的第二个泛型{}TDataselect函数返回的数据类型未指定时取TQueryFnDataTQueryFnDataTResponse变更函数返回的数据类型未指定时取TDataTDataTResponseError变更错误类型未指定时取TErrorTError日常用法中像示例那样只传前三个参数IPost, HttpError, IPost即可获得良好的类型推导。六、适用边界与实践建议headless 场景首选useStepsForm位于refinedev/react-hook-form不绑定任何 UI 库步骤导航按钮、布局完全由你掌控如果使用 Ant Design / MUI / MantineRefine 在 documentation/docs/examples/form/antd/useStepsForm.md、documentation/docs/examples/form/mui/useStepsForm.md、documentation/docs/examples/form/mantine/useStepsForm.md 中分别提供了对应的useStepsForm集成示例。校验粒度默认只在向前切步时触发全量校验若业务要求返回上一步也校验例如步骤间有强依赖的必填项开启isBackValidate: true。创建与编辑共用组件从示例看PostCreate与PostEdit的渲染结构完全一致可将renderFormByStep抽成共享组件仅通过refineCore.query的有无区分回显逻辑降低重复代码。关系字段回显涉及category.id这类嵌套字段时推荐ControlleruseSelect的组合useSelect的defaultValue依赖query?.data?.data需要等详情查询完成后再渲染示例中通过formLoading提前return来保证。总结useStepsForm以极少的内部状态一个useState在useForm之上叠加了步骤管理能力currentStep定位当前步骤、gotoStep负责带校验的跳转、useEffect自动完成编辑回填且不覆盖用户已改字段。结合示例 examples/form-react-hook-form-use-steps-form、Hook 文档 documentation/docs/packages/react-hook-form/use-steps-form/index.md 与源码 packages/react-hook-form/src/useStepsForm/index.ts 阅读你可以快速把多步骤表单能力复用到自己的 Refine v5 项目中。【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/11/9 19:14:08

SerenityOS 移植 ProTracker 2 克隆:pt2-clone 补丁深度解析

SerenityOS 移植 ProTracker 2 克隆:pt2-clone 补丁深度解析 【免费下载链接】serenity The Serenity Operating System 🐞 项目地址: https://gitcode.com/GitHub_Trending/se/serenity SerenityOS 通过 Ports 系统将大量第三方软件移植到自有内…

2026/11/9 12:49:44

JVM基础入门:从运行机制到主流虚拟机实现

JVM基础入门:从运行机制到主流虚拟机实现JVM基础入门:从运行机制到主流虚拟机实现一、什么是 JVM?二、为什么 Java 能够跨平台?三、JVM 的三大核心功能1. 解释和运行字节码四、JVM 的内存管理能力1. 自动分配内存2. 自动垃圾回收五…

2026/11/11 14:39:34

青海电工证报名流程良心建议:怕考不过白交培训费?这几点必须知道

青海电工证报名流程良心建议:怕考不过白交培训费?这几点必须知道 怕考不过白交培训费?青海电工证报名流程其实没你想象中那么复杂,但关键细节一步错全盘皆输。别让培训费打了水漂,也别让时间白白浪费。本文从平顶山中小施工企业负责人的角度出发,结合多…

2026/11/11 14:39:34

景德镇电工证在哪里考怎么转河南证?2026河南最新政策全解析

景德镇电工证在哪里考怎么转河南证?2026河南最新政策全解析 之前考的证在外省能不能转过来?这个问题几乎每个打算报考电工证的同行都问过,尤其是在像景德镇这样跨省工作的从业者,更担心自己的证书在河南是否还能用。2026年河南最新政策已经出台,…

2026/11/11 14:39:34

乐昌电工证换证地点怎么找?全国通用证书别再走弯路了

乐昌电工证换证地点怎么找?全国通用证书别再走弯路了 怕考不过白交培训费?别急,你不是一个人。在乐昌电工证换证地点的问题上,很多人都遇到过类似的困惑:不知道该去哪儿换证、换证流程复杂、担心证件不被认可、甚至因为换证不及时而被停工罚款。如果你是…

2026/11/11 14:39:34

永顺电工证费用标准怎么查?证书过期了不知道怎么复审延期

永顺电工证费用标准怎么查?证书过期了不知道怎么复审延期 你是不是也遇到过这种情况?手里的电工证眼看就要到期了,可你根本不知道怎么复审,甚至连网上查询都不太懂,生怕一不小心就丢了证,影响工作甚至被单位追责。今天咱们就从南阳一个应届工程类毕业生…

2026/11/11 14:34:34

办理贵州电工证南明区需要什么材料

电工证在贵州南明区考不下来?工地太忙根本没时间复习考试,千万别踩坑! 工地太忙根本没时间复习考试,这几乎是每个建筑工人心头的痛。尤其是像在安阳这样常年奔波在工地的工人,每天忙得脚不沾地,哪还有时间准备考试?但问题是, 贵州电工证南明区…

2026/11/11 12:42:59

电工证丢失还能补吗?千万别踩坑,丢了证等于失业

电工证丢失还能补吗?千万别踩坑,丢了证等于失业 你正在找工作,但手里的电工证突然丢了,这下怎么办?别急,先别慌, 电工证丢失还能补吗 ?答案是肯定的,但 千万别踩坑 ,补办流程如果不了解,可能会耽误你上岗、甚至影响到你的收入。…

2026/11/11 0:02:26

考低压电工证通过率考试真题与备考技巧

低压电工证通过率高不高?学历不高也能考,值不值得考? 你是不是总听说电工证能帮你找高薪工作,但又担心自己学历不够,考不上?其实,低压电工证考试门槛并没有你想象中那么高,关键是方法对不对。这篇文章就告诉你, 考低压电工证通过率到底有多高 ,…

2026/11/11 0:02:26

低压电工证学时千万别踩坑,濮阳毕业生怎么避坑报名

低压电工证学时千万别踩坑,濮阳毕业生怎么避坑报名 你是不是也和我一样,刚毕业就听说低压电工证学时必须达标才能上岗?可一打听,发现报名机构五花八门,价格参差不齐, 不知道去哪报名怕被中介坑…

2026/11/10 20:08:15

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/10 10:29:37

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑