Zod refine在react-hook-form中可选数组字段不显示错误
解决React Hook Form + Zod 多字段至少填一个的错误提示问题
问题根源
你当前的Zod Schema中,.refine的path参数同时指定了['warehouses', 'clusters'],这不符合Zod的规则——path只能指向单个字段的路径,无法同时关联多个字段。这导致React Hook Form无法将验证错误正确绑定到对应的表单字段上,所以字段上不显示错误提示。
修复方案
方案1:使用superRefine给两个字段分别添加错误
通过superRefine可以手动控制错误的添加,将同一个错误信息关联到两个字段上,这样两个FormField都会显示错误提示:
import { z } from 'zod'; const schema = z.object({ warehouses: z.array(z.string()).optional(), clusters: z.array(z.string()).optional(), }).superRefine((data, ctx) => { const hasWarehouses = (data.warehouses?.length ?? 0) > 0; const hasClusters = (data.clusters?.length ?? 0) > 0; if (!hasWarehouses && !hasClusters) { // 给warehouses字段添加错误 ctx.addIssue({ code: z.ZodIssueCode.custom, message: '至少需要选择一个仓库或集群', path: ['warehouses'], }); // 给clusters字段添加错误 ctx.addIssue({ code: z.ZodIssueCode.custom, message: '至少需要选择一个仓库或集群', path: ['clusters'], }); } });
方案2:将错误绑定到表单根节点,显示全局错误
如果不需要在单个字段上显示错误,而是在表单顶部显示全局提示,可以将path设为空数组(指向表单根):
const schema = z.object({ warehouses: z.array(z.string()).optional(), clusters: z.array(z.string()).optional(), }).refine((data) => (data.warehouses?.length ?? 0) > 0 || (data.clusters?.length ?? 0) > 0, { message: '至少需要选择一个仓库或集群', path: [], // 错误绑定到表单根节点 });
然后在表单中获取根错误并显示:
const { formState: { errors } } = form; {errors.root && ( <div className="text-red-500 mb-4"> {errors.root.message} </div> )}
方案3:只给其中一个字段绑定错误
如果你只需要在其中一个字段上显示错误,比如warehouses,可以将path设为['warehouses']:
const schema = z.object({ warehouses: z.array(z.string()).optional(), clusters: z.array(z.string()).optional(), }).refine((data) => (data.warehouses?.length ?? 0) > 0 || (data.clusters?.length ?? 0) > 0, { message: '至少需要选择一个仓库或集群', path: ['warehouses'], // 错误只绑定到warehouses字段 });
验证生效
修改Schema后,确保你的FormField组件中正确使用了fieldState.error来显示错误信息(你提到组件本身没问题,这里确认逻辑):
<FormField form={form} label={'Warehouses'} name="warehouses" render={({ field, fieldState }) => ( <> <AutofillMultibox field={field} choices={['South', 'East', 'West'].map((e) => ({ label: e, value: e, }))} /> {fieldState.error && ( <p className="text-red-500 text-sm mt-1"> {fieldState.error.message} </p> )} </> )} />
内容的提问来源于stack exchange,提问作者Nikita Dmitriev
相关产品推荐
相关产品推荐

