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

React多步骤表单:Hook Form与Zod分步骤条件验证可行性问询

问题描述

我正在使用React开发多步骤表单,采用React Hook Form与Zod进行验证。为简化说明,假设表单仅包含两步、三个字段:

第一步

必填的下拉选择框,包含两个选项:

<Select placeholder='Select option' {...register("select")}>
  <option value='A'>Option A</option>
  <option value='B'>Option B</option>
</Select>

第二步

包含两个输入框,显示状态取决于第一步的选择结果:

{inputOneVisible && <Input placeholder='Input 1' {...register("inputOne")}/>}
{inputTwoVisible && <Input placeholder='Input 2' {...register("inputTwo")}/>}

两个输入框各自有不同的验证规则,但我需要根据第一步的选择结果仅验证其中一个。目前我为每个步骤单独定义了Zod Schema:

export const stepOneSchema = z.object({
  options: z.string()
});
export const stepTwoSchema = z.object({
  input1: z.string(),
  input2: z.string()
});

补充说明:各步骤为独立表单,由容器组件根据当前步骤号渲染对应表单。请问这种情况下能否实现上述条件验证?


解决方案

完全可以实现这种条件验证,核心是利用Zod的条件分支语法结合React Hook Form的表单值,动态调整验证规则适配多步骤独立表单的结构。

具体实现步骤:

  1. 共享第一步的选择值
    由于步骤是独立表单,需要把第一步的select值存储到容器组件的状态(比如useState),或通过React Context传递,确保第二步表单能获取到该值来决定验证规则。

  2. 重构第二步的Zod Schema
    用Zod的动态生成逻辑或superRefine实现条件验证,示例如下:

    • 动态生成Schema写法:根据第一步选择的选项,只保留需要验证的字段
      // 先定义各输入框的独立验证规则
      const inputOneRule = z.string().min(3, "Input1至少需要3个字符");
      const inputTwoRule = z.string().email("Input2必须是有效的邮箱格式");
      
      // 根据第一步选择值生成对应Schema
      export const getStepTwoSchema = (selectedOption: string) => 
        z.object({
          ...(selectedOption === 'A' ? { inputOne: inputOneRule } : {}),
          ...(selectedOption === 'B' ? { inputTwo: inputTwoRule } : {})
        });
      
    • superRefine精细控制写法:适合更复杂的条件验证场景
      export const stepTwoSchema = z.object({
        select: z.string(), // 需要传入第一步的选择值
        inputOne: z.string().optional(),
        inputTwo: z.string().optional()
      }).superRefine((data, ctx) => {
        if (data.select === 'A') {
          if (!data.inputOne) {
            ctx.addIssue({
              code: z.ZodIssueCode.custom,
              message: "Input1不能为空",
              path: ["inputOne"]
            });
          }
          // 可在此添加inputOne的其他验证逻辑
        } else if (data.select === 'B') {
          const emailCheck = z.string().email().safeParse(data.inputTwo);
          if (!data.inputTwo || !emailCheck.success) {
            ctx.addIssue({
              code: z.ZodIssueCode.custom,
              message: "Input2必须是有效的邮箱",
              path: ["inputTwo"]
            });
          }
        }
      });
      
  3. 在第二步表单中应用动态Schema
    在第二步组件中获取第一步的选择值,生成对应Schema后传递给React Hook Form的解析器:

    import { useForm } from "react-hook-form";
    import { zodResolver } from "@hookform/resolvers/zod";
    import { getStepTwoSchema } from "./schemas";
    
    const StepTwoForm = ({ selectedOption }) => {
      const formSchema = getStepTwoSchema(selectedOption);
      const { register, handleSubmit, formState: { errors } } = useForm({
        resolver: zodResolver(formSchema)
      });
    
      const onSubmit = (data) => {
        // 处理第二步提交逻辑
      };
    
      return (
        <form onSubmit={handleSubmit(onSubmit)}>
          {selectedOption === 'A' && (
            <Input 
              placeholder='Input 1' 
              {...register("inputOne")} 
              error={errors.inputOne?.message}
            />
          )}
          {selectedOption === 'B' && (
            <Input 
              placeholder='Input 2' 
              {...register("inputTwo")} 
              error={errors.inputTwo?.message}
            />
          )}
          <button type="submit">提交第二步</button>
        </form>
      );
    };
    
  4. 注意事项

    • 确保第一步的选择值能正确传递到第二步组件,避免验证时缺失条件值
    • 无需验证的字段可设为optional(),或在动态生成Schema时直接不包含该字段,避免Zod默认的必填校验

内容的提问来源于stack exchange,提问作者PoCiTo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 11:35:13