Zod+react-hook-form表单验证问题:至少填邮箱/手机号但校验失效
问题原因
你最后那个全局refine的逻辑写反了!Zod的refine规则是:返回false时才会触发错误提示,而你现在的代码是当邮箱和手机号都为空时返回true,这就相当于告诉Zod“这种情况是合法的”,自然不会触发校验错误。
修正方案
把全局refine的判断条件取反,改成当两者都为空时返回false,这样就能正确拦截空提交的情况:
const emailValidator = z.string().email() export const contactFirstStepSchema = z .object({ email: z .string() .refine(v => (v ? emailValidator.safeParse(v).success : true), "Invalid email"), phone: z.string().refine(value => { if (value) return isValidPhoneNumber(value, 'FR') return true }, "Invalid number"), description: z.string().optional() }) .partial() .refine(({ email, phone }) => !( (email === undefined || email === '') && (phone === undefined || phone === '') ), { message: "At least one field should be provided (or both).", // 可选:指定错误关联到哪个字段,方便react-hook-form定位显示错误 path: ['email'] // 也可以填['phone'],如果想让错误全局显示可以省略 })
额外优化建议
你可以简化邮箱和手机号的校验写法,不用手动判断空值再校验,Zod本身支持optional()链式处理:
- 邮箱直接写成
z.string().email("Invalid email").optional(),空值会被视为合法,非空值才会校验格式 - 手机号同理,用
optional()标记空值合法,非空时再校验格式
优化后的代码示例:
export const contactFirstStepSchema = z .object({ email: z.string().email("Invalid email").optional(), phone: z.string().refine(value => { if (!value) return true return isValidPhoneNumber(value, 'FR') }, "Invalid number").optional(), description: z.string().optional() }) .refine(({ email, phone }) => !( !email && !phone ), { message: "At least one field should be provided (or both).", path: ['email'] })
这里去掉了.partial(),因为每个字段都已标记为optional(),效果一致且代码更清晰。
内容的提问来源于stack exchange,提问作者Ahmed Zrouqui
相关产品推荐
相关产品推荐

