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

如何基于React状态在新版Zod中实现条件字段校验?

新版Zod实现React状态驱动的条件必填校验

你需要基于一个取值为true/false的React状态,控制表单字段的必填性:当状态为true时目标字段为必填项,状态为false时则非必填。你之前找到的旧版Zod方案依赖ctx.parent属性,但新版Zod已移除该属性,这里提供两种适配新版的实现方式:

方案一:动态生成Schema(推荐)

直接根据外部状态动态生成对应的校验规则,这是最简洁高效的方式,无需额外处理状态同步:

import { useState } from 'react';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';

// 定义生成Schema的函数,接收外部状态参数
const buildFormSchema = (isTargetFieldRequired: boolean) => 
  z.object({
    targetField: isTargetFieldRequired 
      ? z.string().trim().min(1, '此字段为必填项') 
      : z.string().trim().optional(),
    otherField: z.string().optional()
  });

export default function ConditionalRequiredForm() {
  const [isTargetRequired, setIsTargetRequired] = useState(false);
  // 根据当前状态生成对应的Schema
  const formSchema = buildFormSchema(isTargetRequired);

  const { register, handleSubmit, formState: { errors } } = useForm({
    resolver: zodResolver(formSchema)
  });

  const handleFormSubmit = (data: z.infer<ReturnType<typeof buildFormSchema>>) => {
    console.log('提交数据:', data);
  };

  return (
    <form onSubmit={handleSubmit(handleFormSubmit)}>
      <div>
        <label>
          <input 
            type="checkbox" 
            checked={isTargetRequired}
            onChange={(e) => setIsTargetRequired(e.target.checked)}
          />
          标记目标字段为必填
        </label>
      </div>

      <div>
        <input {...register('targetField')} placeholder="目标字段" />
        {errors.targetField && <p style={{ color: 'red' }}>{errors.targetField.message}</p>}
      </div>

      <div>
        <input {...register('otherField')} placeholder="其他字段" />
      </div>

      <button type="submit">提交表单</button>
    </form>
  );
}

方案二:合并外部状态到表单数据后校验

如果需要处理更复杂的多字段关联校验逻辑,可以把外部状态合并到表单数据中,在superRefine里直接读取状态进行校验:

import { useState, useEffect } from 'react';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';

// 定义包含外部状态映射字段的Schema
const formSchema = z.object({
  targetField: z.string().trim().optional(),
  otherField: z.string().optional(),
  isTargetRequired: z.boolean()
}).superRefine((data, ctx) => {
  // 直接从data中获取状态,无需依赖ctx.parent
  if (data.isTargetRequired && !data.targetField) {
    ctx.addIssue({
      path: ['targetField'],
      code: z.ZodIssueCode.custom,
      message: '启用必填时此字段不能为空'
    });
  }
});

export default function ComplexConditionalForm() {
  const [isTargetRequired, setIsTargetRequired] = useState(false);

  const { register, handleSubmit, formState: { errors }, setValue } = useForm({
    resolver: zodResolver(formSchema),
    defaultValues: { isTargetRequired }
  });

  // 同步外部状态到表单内部字段
  useEffect(() => {
    setValue('isTargetRequired', isTargetRequired);
  }, [isTargetRequired, setValue]);

  const handleFormSubmit = (data: z.infer<typeof formSchema>) => {
    // 提交时剔除状态字段,只保留业务数据
    const { isTargetRequired: _, ...submitData } = data;
    console.log('提交数据:', submitData);
  };

  return (
    <form onSubmit={handleSubmit(handleFormSubmit)}>
      <div>
        <label>
          <input 
            type="checkbox" 
            checked={isTargetRequired}
            onChange={(e) => setIsTargetRequired(e.target.checked)}
          />
          标记目标字段为必填
        </label>
      </div>

      <div>
        <input {...register('targetField')} placeholder="目标字段" />
        {errors.targetField && <p style={{ color: 'red' }}>{errors.targetField.message}</p>}
      </div>

      <div>
        <input {...register('otherField')} placeholder="其他字段" />
      </div>

      <button type="submit">提交表单</button>
    </form>
  );
}

方案说明

  • 方案一逻辑直观,性能更优,适合简单的单字段条件必填场景,状态变化时自动更新校验规则。
  • 方案二适合复杂校验场景,比如需要同时结合多个外部状态或表单字段进行判断的情况,通过合并状态到表单数据实现统一校验。

内容的提问来源于stack exchange,提问作者Александр Кос

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 14:25:18