修复Zod与React Hook Form中的手机号校验错误提示问题
解决Zod+react-hook-form手机号校验始终显示格式错误的问题
问题出在你的手机号校验逻辑里缺少了空字符串拦截,导致空值会进入后续的格式校验流程,最终触发格式错误提示而非必填提示。
问题根源
你的contactNumber字段仅通过string({ required_error })声明必填,但zod.string()本身允许空字符串通过校验。空字符串会被后续的transform处理成"+",而isValidPhoneNumber("+")返回false,因此无论输入为空还是格式错误,都会触发"无效的手机号格式"提示。
修复方案
- 给
contactNumber添加.nonempty()验证,拦截空字符串并触发必填提示; - 移除
Controller中的rules={{ required: true }},统一由Zod处理校验逻辑,避免冲突。
修改后的完整代码如下:
import { isValidPhoneNumber } from "libphonenumber-js"; ... const schema = zod.object({ firstName: zod .string({ required_error: "First name is required.", }) .nonempty("First name is required."), lastName: zod .string({ required_error: "Last name is required.", }) .nonempty("Last name is required."), contactNumber: zod .string({ required_error: "手机号为必填项", }) .nonempty("手机号为必填项") // 新增:拦截空字符串,触发必填提示 .transform((value) => `+${value}`) .refine(isValidPhoneNumber, "无效的手机号格式") .transform((value) => value.slice(1)), }); ... <Controller control={control} name="contactNumber" // 移除rules={{ required: true }},由Zod处理必填校验 render={({ field: { ref, ...field }, ...props }) => ( <StyledPhoneInput enableSearch enableTerritories specialLabel="Mobile number " inputProps={{ ref, required: true, // 仅保留HTML原生必填标记,用于UI提示 placeholder: "+84", }} country="vn" autoFormat field={field} muiProps={{ required: true, fullWidth: true }} {...props} /> )} />
逻辑说明
.nonempty()会在输入为空字符串时直接抛出必填错误,阻止后续的transform和refine执行,确保空值时显示"手机号为必填项";- 仅当输入非空字符串时,才会进入格式校验流程:先给手机号添加
+前缀(适配libphonenumber-js的校验要求),校验通过后再移除前缀,保证最终存储的手机号格式符合需求; - 移除
Controller的rules.required,避免react-hook-form的内置必填校验与Zod的校验逻辑冲突,统一由Zod管理所有业务校验规则。
内容的提问来源于stack exchange,提问作者Joseph
相关产品推荐
相关产品推荐

