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

使用react-hook-form与Yup实现多步骤表单提交失败求助

解决React Hook Form结合Yup实现多步骤表单的提交问题

问题根源

你当前的实现中,当处于最后一步(currStep=3)时,resolver的表达式会返回false,而非有效的Yup Schema:

resolver: yupResolver(currStep === 1 ? schema1 : currStep === 2 && scheme2),

当currStep=3时,currStep === 2 && scheme2结果为false,yupResolver无法识别布尔值,导致提交出错。

另外,你提到的嵌套Schema方案,核心是要实现分步校验指定嵌套字段,下面提供两种可行解决方案:


方案一:修复分步Schema的Resolver逻辑

保留现有扁平字段结构,为最后一步提供包含所有字段的完整Schema,确保每一步都传入有效的Yup Schema:

修改后的核心代码

const RegisterClient = props => {
  const [currStep, setCurrStep] = React.useState(1)

  const schema1 = Yup.object().shape({
    company_name: Yup.string().required("Name required"),
    email: Yup.string().email().required("Email required"),
    password: Yup.string()
      .matches(
        passwordRules,
        "Password must contain at least 8 digits (one uppercase, one lowercase, one number and a symbol)."
      )
      .required("Required"),
  })

  const schema2 = Yup.object().shape({
    representative_name: Yup.string().required(
      "Representative name is required"
    ),
    representative_email: Yup.string()
      .email()
      .required("Representative email required"),
  })

  // 定义完整的提交Schema,合并所有字段规则
  const fullSchema = Yup.object().shape({
    ...schema1.fields,
    ...schema2.fields
  })

  const {
    control,
    handleSubmit,
    formState: { errors },
    getValues,
  } = useForm({
    resolver: yupResolver(
      currStep === 1 ? schema1 : 
      currStep === 2 ? schema2 : 
      fullSchema
    ),
    defaultValues: {
      company_name: "",
      email: "",
      password: "",
      representative_name: "",
      representative_email: "",
    },
  })

  // 其余代码保持不变...
}

方案二:使用嵌套Schema实现分步校验

如果偏好嵌套字段结构,可以通过动态构建Schema来分步校验指定嵌套部分:

步骤1:定义嵌套Schema与对应默认值

const fullSchema = Yup.object().shape({
  company: Yup.object().shape({
    company_name: Yup.string().required("Name required"),
    email: Yup.string().email().required("Email required"),
    password: Yup.string()
      .matches(
        passwordRules,
        "Password must contain at least 8 digits (one uppercase, one lowercase, one number and a symbol)."
      )
      .required("Required"),
  }),
  representative: Yup.object().shape({
    representative_name: Yup.string().required(
      "Representative name is required"
    ),
    representative_email: Yup.string()
      .email()
      .required("Representative email required"),
  })
})

// 在useForm中使用嵌套默认值
const {
  control,
  handleSubmit,
  formState: { errors },
  getValues,
} = useForm({
  resolver: yupResolver(
    currStep === 1 ? Yup.object({ company: fullSchema.fields.company }) :
    currStep === 2 ? Yup.object({ representative: fullSchema.fields.representative }) :
    fullSchema
  ),
  defaultValues: {
    company: {
      company_name: "",
      email: "",
      password: ""
    },
    representative: {
      representative_name: "",
      representative_email: ""
    }
  },
})

步骤2:修改子组件的字段名称

更新Step1和Step2中的Controller的name为嵌套路径:

// Step1示例
<Controller
  name="company.company_name"
  control={control}
  render={({ field }) => (
    <Input
      {...field}
      placeholder="Company Name"
      type="text"
      invalid={Boolean(errors?.company?.company_name)}
    />
  )}
/>
{errors?.company?.company_name && (
  <FormFeedback>{errors.company.company_name.message}</FormFeedback>
)}

// Step2示例同理,字段名改为"representative.representative_name"等

额外优化建议

  • 在nextStep函数中,先调用trigger校验当前步骤的字段,通过后再切换步骤:
    async function nextStep() {
      const isValid = await trigger(currStep ===1 ? Object.keys(schema1.fields) : Object.keys(schema2.fields))
      if (isValid) {
        setCurrStep(curr => curr +1)
      }
    }
    
  • 移除onSubmit中切换步骤的逻辑,让nextStep单独处理步骤切换,onSubmit仅负责最终提交:
    const onSubmit = async user => {
      console.log("user: ", user)
      // 这里执行实际的提交逻辑,比如API请求
    }
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 21:45:37