TanStack Form 动态校验(Dynamic Validation)指南:onDynamic 与 revalidateLogic 完整解析
发布时间:2026/9/17 4:40:06来源:尧图网络
TanStack Form 动态校验Dynamic Validation指南onDynamic 与 revalidateLogic 完整解析【免费下载链接】form Headless, performant, and type-safe form state management for TS/JS, React, Vue, Angular, Solid, and Lit.项目地址: https://gitcode.com/GitHub_Trending/form/form本指南讲解 TanStack React Form 的动态校验能力通过onDynamic校验函数与revalidateLogic校验逻辑让表单在首次提交前与首次提交后采用完全不同的校验时机与规则。阅读本文后你将掌握如何配置validationLogic、按提交状态动态切换change/blur/submit三种校验模式、读取errorMap中的动态错误以及在表单级与字段级、同步与异步、Zod / Valibot 标准 Schema 场景下组合使用onDynamic。什么是动态校验大多数表单的校验规则是静态的只要配置了onChange或onBlur校验就会在对应事件发生时执行。但实际产品中一个非常常见的需求是校验规则随表单状态动态变化——最典型的就是“首次提交前不要打扰用户首次提交之后才开始严格校验并即时反馈”。TanStack Form 通过onDynamic校验函数来满足这一场景。它的核心特征有两个规则动态化onDynamic可以根据当前表单值、提交次数等任意条件动态返回不同的校验结果时机动态化配合revalidateLogic()校验触发时机会在“首次提交前”和“首次提交后”之间自动切换行为上非常接近 React Hook Form 的mode/reValidateMode机制这一点在 ValidationLogic.ts 的源码注释中有明确说明。前提为什么必须配置revalidateLogic()官方文档中有一条醒目的注意事项默认情况下onDynamic不会被调用因此你必须在useForm的validationLogic选项中传入revalidateLogic()。这一点可以从源码中得到印证在 FormApi.ts 中表单校验默认使用this.options.validationLogic || defaultValidationLogic即默认的校验逻辑是defaultValidationLogic。而defaultValidationLogic在 ValidationLogic.ts 中只会根据事件类型挑选onMount、onChange、onBlur、onSubmit、onServer这几类校验器完全不会触碰onDynamic。也就是说onDynamic只有在一个自定义的ValidationLogicFn例如revalidateLogic中才会被主动取出并执行。最基本的用法如下import { revalidateLogic, useForm } from tanstack/react-form const form useForm({ defaultValues: { firstName: , lastName: , }, // 如果省略此行onDynamic 将不会被调用 validationLogic: revalidateLogic(), validators: { onDynamic: ({ value }) { if (!value.firstName) { return { firstName: A first name is required } } return undefined }, }, })revalidateLogic与useForm均通过tanstack/react-form导出而tanstack/react-form的入口 index.ts 又会export * from tanstack/form-core因此revalidateLogic的实际实现位于跨框架共享的form-core包中React、Vue、Angular、Solid、Lit 等框架都可以使用同一套动态校验逻辑。revalidateLogic 的两个参数mode 与 modeAfterSubmissionrevalidateLogic接收两个可选参数用于指定校验的触发时机参数含义可选值默认值mode首次提交前使用的校验模式change每次变更、blur失焦、submit提交submitmodeAfterSubmission首次提交后使用的校验模式change每次变更、blur失焦、submit提交change两个参数在 ValidationLogic.ts 的RevalidateLogicProps接口中有类型定义与 JSDoc 默认值说明。这一组合带来的直观体验是提交前表单静默用户可自由填写直到点击提交才校验提交后一旦校验失败用户修改字段时立即重新校验错误提示随输入实时消失或出现。例如希望提交前仅在提交时校验提交后在失焦时重新校验const form useForm({ // ... validationLogic: revalidateLogic({ mode: submit, modeAfterSubmission: blur, }), // ... })底层原理submissionAttempts 门控revalidateLogic之所以能“感知”表单是否已提交靠的是表单状态中的submissionAttempts字段。其核心实现位于 ValidationLogic.tsexport const revalidateLogic ({ mode submit, modeAfterSubmission change, }: RevalidateLogicProps {}): ValidationLogicFn (props) { // ...取到 onDynamic / onDynamicAsync 校验器cause 固定为 dynamic // 根据提交次数决定使用哪个模式 const submissionAttempts props.group ? props.group.state.meta.submissionAttempts : props.form.state.submissionAttempts const modeToWatch submissionAttempts 0 ? mode : modeAfterSubmission // 当前事件命中目标模式或本身是 submit时才把动态校验器加入执行列表 if ([modeToWatch, submit].includes(props.event.type)) { validatorsToAdd.push(dynamicValidator) } // ...其余校验器按 defaultValidationLogic 处理 }从源码可以提炼出三点关键行为以submissionAttempts 0为分界为 0 时采用mode大于 0 时采用modeAfterSubmission事件命中目标模式或事件本身就是submit时onDynamic才会被加入本次执行队列。这解释了为何默认mode: submit下首次提交前只有点击提交才会触发动态校验异步版本自动切换代码通过props.event.async区分同步/异步校验周期异步周期取onDynamicAsync同步周期取onDynamic二者不会混淆。此外ValidationLogic.ts 中的ValidationLogicProps还展示了revalidateLogic依赖的完整上下文form表单实例、可选的group用于FormGroupApi自身校验时以组为单位门控、validators校验器集合以及event事件类型blur/change/submit/mount/server与async标记等。这意味着你甚至可以手写自定义的ValidationLogicFn实现比revalidateLogic更细粒度的门控策略测试文件中就提供了一个按字段isBlurred状态动态切换模式的示例见 DynamicValidation.spec.ts。访问动态校验错误errorMap.onDynamiconDynamic产生的错误与onChange、onBlur一样存放在表单状态的errorMap对象中以校验器名称为键访问function App() { const form useForm({ // ... validationLogic: revalidateLogic(), validators: { onDynamic: ({ value }) { if (!value.firstName) { return { firstName: A first name is required } } return undefined }, }, }) return p{form.state.errorMap.onDynamic?.firstName}/p }当表单级校验器返回{ firstName: ... }这种“字段名 → 错误信息”的对象时form.state.errorMap.onDynamic.firstName就是对应字段的错误。如果校验通过返回undefined错误即被清除。与其他校验逻辑混用onDynamic并不是排他性的它可以与onChange、onBlur等校验逻辑共存各司其职。例如让onChange校验姓名字段、onDynamic校验姓氏字段import { revalidateLogic, useForm } from tanstack/react-form function App() { const form useForm({ defaultValues: { firstName: , lastName: , }, validationLogic: revalidateLogic(), validators: { onChange: ({ value }) { if (!value.firstName) { return { firstName: A first name is required } } return undefined }, onDynamic: ({ value }) { if (!value.lastName) { return { lastName: A last name is required } } return undefined }, }, }) return ( div p{form.state.errorMap.onChange?.firstName}/p p{form.state.errorMap.onDynamic?.lastName}/p /div ) }这里onChange与onDynamic的错误分别通过errorMap.onChange和errorMap.onDynamic读取互不干扰。从revalidateLogic的实现也可以看出它只是追加动态校验器当动态校验器未被命中时会退回执行defaultValidationLogic挑选出的校验器命中时则返回“默认校验器 动态校验器”的组合ValidationLogic.ts因此两者天然兼容。在字段上使用 onDynamic动态校验同样可以应用于字段级。字段级的onDynamic通过form.Field的validators选项配置错误从field.state.meta.errorMap.onDynamic读取function App() { const form useForm({ defaultValues: { name: , age: 0, }, validationLogic: revalidateLogic(), onSubmit({ value }) { alert(JSON.stringify(value)) }, }) return ( form onSubmit{(e) { e.preventDefault() e.stopPropagation() form.handleSubmit() }} form.Field name{age} validators{{ onDynamic: ({ value }) value 18 ? undefined : Age must be greater than 18, }} children{(field) ( div input typenumber onChange{(e) field.handleChange(e.target.valueAsNumber)} onBlur{field.handleBlur} value{field.state.value} / p style{{ color: red }} {field.state.meta.errorMap.onDynamic} /p /div )} / button typesubmitSubmit/button /form ) }注意字段级onDynamic返回的是单个错误信息字符串而非{ field: message }对象因此访问路径是field.state.meta.errorMap.onDynamic。这一点同样体现在 FieldApi.ts 的类型定义中字段级校验器包含onDynamic与onDynamicAsync以及用于控制异步防抖时长的onDynamicAsyncDebounceMs。测试文件 DynamicValidation.spec.ts 也专门验证了字段级动态校验的完整生命周期提交前修改不报错 → 首次提交后立即报错 → 再次修改后错误即时清除。异步校验与防抖onDynamicAsync与服务端/外部系统联动的校验如用户名是否被占用通常需要异步执行。onDynamicAsync与其它异步校验器用法一致并且可以通过onDynamicAsyncDebounceMs配置防抖避免每次输入都触发一次昂贵请求const form useForm({ defaultValues: { username: , }, validationLogic: revalidateLogic(), validators: { onDynamicAsyncDebounceMs: 500, // 将异步校验防抖 500ms onDynamicAsync: async ({ value }) { if (!value.username) { return { username: Username is required } } // 模拟一次异步校验 const isValid await validateUsername(value.username) return isValid ? undefined : { username: Username is already taken } }, }, })onDynamicAsyncDebounceMs正是上文提到的字段级/表单级校验器配置中的防抖选项见 FieldApi.ts。异步动态校验的时机门控与同步版完全一致——在 ValidationLogic.ts 中异步事件周期会取onDynamicAsync同步事件周期取onDynamic。测试 DynamicValidation.spec.ts 通过一个可控 Promise 验证了异步动态校验在提交后正确写入form.state.errorMap.onDynamic。使用标准 SchemaZod / Valibot定义动态规则如果你使用 Zod、Valibot 等实现了 Standard Schema 规范的校验库可以直接把 Schema 传给onDynamic从而用声明式方式定义可随表单状态动态变化的复杂规则import { z } from zod const schema z.object({ firstName: z.string().min(1, A first name is required), lastName: z.string().min(1, A last name is required), }) const form useForm({ defaultValues: { firstName: , lastName: , }, validationLogic: revalidateLogic(), validators: { onDynamic: schema, }, })底层机制是TanStack Form 通过isStandardSchemaValidatorstandardSchemaValidator.ts检测校验器是否实现了 Standard Schema即是否具有~standard属性若命中则由standardSchemaValidators.validate执行schema[~standard].validate(value)并把返回的issues按字段路径转换成表单可消费的错误映射standardSchemaValidator.ts。这意味着 Zod、Valibot 等生态中所有符合该规范的 Schema 都可以无缝嵌入onDynamic。行为验证测试用例佐证revalidateLogic的整套行为在 DynamicValidation.spec.ts 中被逐项验证其中最关键的一条L55-L96完整复现了“RHF 式校验”的预期初始挂载时errorMap.onDynamic为undefined不校验提交前修改字段不产生动态错误mode: submit生效首次handleSubmit()后动态错误出现之后修改字段错误立即清除modeAfterSubmission: change生效。另一条测试L159-L204验证了自定义mode: changemodeAfterSubmission: blur的组合提交前变更即校验、提交后改为失焦才重新校验。若你想调整或实验这些时机行为直接修改测试中的validationLogic配置即可快速验证。小结与适用场景动态校验适合以下场景首次提交前不打扰用户提交后即时反馈默认的mode: submitmodeAfterSubmission: change组合即可提交前轻校验、提交后严格校验例如提交前只校验必填提交后追加长度、格式校验——onDynamic内根据submissionAttempts或传入的value分支返回不同规则异步去重类校验用户名、邮箱、邀请码等需远程确认的字段配合onDynamicAsyncDebounceMs防抖跨字段联动校验在onDynamic中读取整个表单的value根据其他字段的状态决定当前字段的规则。掌握validationLogic: revalidateLogic()这一配置再结合errorMap.onDynamic的错误读取方式你就能让表单的校验体验从“一次性提交后才报错”平滑升级为“提交后即时、精准、动态”的反馈循环。更深入的校验体系如onMount、onServer及其异步版本可继续参阅 validation.md而底层ValidationLogicFn的全部类型定义与自定义入口在 ValidationLogic.ts 中均可找到。【免费下载链接】form Headless, performant, and type-safe form state management for TS/JS, React, Vue, Angular, Solid, and Lit.项目地址: https://gitcode.com/GitHub_Trending/form/form创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网