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

多步骤表单中Formik与Yup校验问题:按钮状态控制异常

解决多步骤Formik表单的按钮禁用与分步校验问题

看起来你遇到的核心问题有两个:一是按钮禁用逻辑搞反了,二是多步骤场景下Formik默认的全局校验不适用——毕竟你只需要校验当前步骤的字段,而不是所有未填写的后续字段。我来一步步帮你搞定:

1. 先修正按钮禁用的逻辑误区

你现在写的disabled={isValid}完全搞反了需求:你想要校验通过时启用按钮,也就是当校验有效时按钮可点击,所以逻辑应该是disabled={!校验有效状态}。但别急,多步骤场景下全局的isValid会因为未填写的后续字段一直为false,所以我们得先解决分步校验的问题。

2. 实现分步字段校验

首先给每个步骤映射对应的需要校验的字段,这样我们只需要检查当前步骤的字段是否符合规则:

// 新增:定义每个步骤对应的校验字段
const stepFieldMap = [
  ['kg'],       // 第1步对应字段
  ['lastName'], // 第2步对应字段
  ['email'],    // 第3步对应字段
  []            // 第4步如果是确认页,可设为空数组
];

然后在Formik的回调中,计算当前步骤的字段是否都通过校验,替代全局的isValid:

// 在Formik的render回调中添加以下逻辑
const currentFields = stepFieldMap[currentStep];
// 检查当前步骤的字段是否都满足校验要求
const isCurrentStepValid = currentFields.every(field => {
  // 字段没有错误,且内容长度≥2(满足min要求),或者已经被用户触碰过(避免初始空值误判)
  return !errors[field] && (values[field].length >= 2 || touched[field]);
});

// 按钮禁用条件:当前步骤校验不通过,或者初始无任何输入
const isNextDisabled = !isCurrentStepValid || (!isDirty && currentFields.some(f => values[f] === ''));

3. 整合完整代码

把上面的逻辑整合到你的代码中,同时区分中间步骤的Next按钮和最后一步的Submit按钮,避免提前触发表单提交:

const pages = [<Page1 />, <Page2 />, <Page3 />, <Page4 />];
// 新增步骤字段映射
const stepFieldMap = [
  ['kg'],
  ['lastName'],
  ['email'],
  []
];

const SignupSchema = Yup.object().shape({
  kg: Yup.string()
    .min(2, "Too Short!")
    .max(50, "Too Long!")
    .required("Required"),
  lastName: Yup.string()
    .min(2, "Too Short!")
    .max(50, "Too Long!")
    .required("Required"),
  email: Yup.string()
    .email("Invalid email")
    .required("Required")
});

const SignUp = () => {
  const currentStep = useSelector((state: RootState) => state);
  const dispatch = useDispatch();

  return (
    <>
      <Logo />
      <div className="signup-container">
        <Formik
          initialValues={{ kg: "", lastName: "", email: "" }}
          onSubmit={values => {
            setTimeout(() => {
              alert(JSON.stringify(values, null, 2));
            }, 500);
          }}
          validationSchema={SignupSchema}
        >
          {({ errors, touched, values, isDirty }) => {
            const currentFields = stepFieldMap[currentStep];
            const isCurrentStepValid = currentFields.every(field => {
              return !errors[field] && (values[field].length >= 2 || touched[field]);
            });
            const isNextDisabled = !isCurrentStepValid || (!isDirty && currentFields.some(f => values[f] === ''));

            return (
              <Form>
                <LinearProgress variant="determinate" />
                <div className="questions">{pages[currentStep]}</div>
                <button 
                  type="button" 
                  onClick={() => dispatch(decrement())}
                  disabled={currentStep === 0} // 第一步禁用上一步按钮
                >
                  Prev
                </button>
                {/* 区分最后一步的提交按钮和中间步骤的下一步按钮 */}
                {currentStep === pages.length - 1 ? (
                  <button type="submit" disabled={!isDirty || !isCurrentStepValid}>
                    Submit
                  </button>
                ) : (
                  <button 
                    type="button" 
                    disabled={isNextDisabled}
                    onClick={() => dispatch(increment())}
                  >
                    Next
                  </button>
                )}
              </Form>
            );
          }}
        </Formik>
      </div>
    </>
  );
};

关键说明

  • 分步校验:通过stepFieldMap关联步骤和字段,只检查当前步骤的字段规则,避免未填写的后续字段导致全局校验失败。
  • 禁用逻辑精准控制:isNextDisabled结合了当前步骤的校验结果和输入状态,确保初始空值时按钮禁用,只有满足字段长度要求且无错误时才启用。
  • 按钮角色区分:最后一步显示Submit按钮触发表单提交,中间步骤用普通按钮切换步骤,符合多步骤表单的交互逻辑。

内容的提问来源于stack exchange,提问作者Gim-Cojocaru Raul-Cristian

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 07:57:35