使用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
相关产品推荐
相关产品推荐

