You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 20:34:58