DEBUG.WIKI
野草云;香港 200Mbps 峰值;不限月流量 · BGP VPS;查看套餐 →
雨云;给项目一台云服务器
晚安云;香港 CN2 ¥15/月

Yup 简单校验也报 async function passed to sync validator

Yup 1.7.1 的 Standard Schema 入口始终返回 Promise,即使规则没有异步 test。把 schema 放到表单的异步入口,分清验证错误与提交数据转换。

表单只校验一个必填字符串,也没有发网络请求,把 Yup schema 放进 TanStack Form 的同步 onChange 后,却报 Error: async function passed to sync validator。先检查调用的是 Yup 自己的同步方法,还是 Standard Schema 入口;两者不是同一个执行约定。

Yup 1.7.1 的 schema['~standard'].validate() 明确是 async 方法,内部等待 schema.validate()。即使所有字段规则都能同步完成,这个适配层仍返回 Promise。规则简单,并不能让同步表单校验器直接接住它。

把 schema 放到表单的异步入口

TanStack Form 提供表单级与字段级的异步校验入口。下面用自定义 Hook 展示配置,schema 校验整个表单对象:

import { useForm } from '@tanstack/react-form';
import { object, string } from 'yup';

const userSchema = object({
  email: string().email('邮箱格式不正确').required('请输入邮箱'),
});

export function useUserForm() {
  return useForm({
    defaultValues: { email: '' },
    validators: {
      onChangeAsync: userSchema,
    },
  });
}

不要同时把同一份 schema 留在原来的同步 onChange 中,否则那条失败路径还在。字段级配置则应使用对应字段的 schema;把整个对象 schema 填给只接收字符串的字段校验,会变成另一种类型错误。

这项调整需要核对项目实际安装的 TanStack Form 是否支持异步 Standard Schema。当前文档提供该入口,不能据此认定任意旧版适配器也能直接照用。Yup 1.7.1 的 Promise 行为本身仍然存在,不是换一个调用位置后变成了同步方法。

已有同步规则可以保留,但要知道同步失败时,异步规则默认可能不会继续运行。只有产品确实要求两条路径都执行,才考虑对应的 asyncAlways 选项。防抖用于减少重复调用,不负责把 Promise 转成同步结果。

必须同步时,单独调用 validateSync

如果这个入口必须立即得到结果,而且整份 Yup schema 确实没有异步 test,可以自行编写同步函数,调用 validateSync() 并把 ValidationError 按表单当前的错误协议返回。这是独立适配,不是修改 ~standard 的返回类型。

遇到异步 test 时,validateSync 不能替它等待。不要只做 TypeScript 类型断言,也不要把 Promise 对象当作校验成功;更不能捕获所有异常后返回“无错误”。预期的字段错误与适配器、程序内部异常应分开处理。

检查时保留空邮箱、格式错误和合法邮箱三类输入,等待异步校验结束后再判断表单错误状态。合法结果不能残留旧错误,错误输入不能提交;快速连续修改字段时,还要确认旧结果不会覆盖新值。TanStack Form 的 schema 校验不会自动把 Yup 的转换结果写回表单值,提交数据若依赖 trim、默认值或类型转换,应沿原提交流程单独取得并使用解析结果。