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

React结合Formik与Yup实现字段可见时的条件校验

问题核心原因
  • Formik的initialValues仅在组件首次挂载时执行一次初始化,后续切换外部isLogin状态时,Formik内部维护的hideName字段值会一直停留在初始的true,Yup的when校验永远拿不到注册状态下hideName: false的值,所以name的必填规则始终不会触发。
  • 切换登录/注册模式时,没有同步更新Formik内部的字段状态,也没有处理name字段的残留值、残留校验错误问题。
  • 切换模式的按钮设置了type="reset",点击时会触发表单默认重置行为,干扰正常的状态逻辑。
  • 代码片段中<Form>标签内部多了一个多余的</section>闭合标签,会导致DOM结构错误。
修复实现

推荐直接用外部状态做校验依赖的方案,不需要额外在表单值里维护hideName字段,逻辑更简洁:

第一步:改造Yup校验规则,直接依赖外部isLogin状态

不用把模式状态存在Formik的values中,直接给Yup传入当前模式状态生成动态校验规则:

// 传入当前isLogin状态,返回对应模式的校验schema
const getAuthFormValidationSchema = (isLogin) => Yup.object().shape({
  name: Yup.string().when([], {
    is: () => !isLogin, // 非登录状态(注册页)时触发必填校验
    then: (schema) => schema.required("Name is required"),
    otherwise: (schema) => schema.notRequired()
  }),
  email: Yup.string().required("Email is required"),
  password: Yup.string().required("Password is required"),
});

第二步:调整Formik组件配置与切换逻辑

去掉表单values里冗余的hideName字段,修正按钮类型,切换模式时同步清理name字段的残留值和错误状态:

const [isLogin, setIsLogin] = useState(true);

// 初始值去掉hideName
const initialAuthFormValues = {
  name: "",
  email: "",
  password: "",
};

<Formik
  initialValues={initialAuthFormValues}
  // 动态传入当前模式对应的校验规则
  validationSchema={getAuthFormValidationSchema(isLogin)}
  onSubmit={submitHandler}
>
  {(formik) => (
    <Form>
      {!isLogin && (
        <TextField
          label="Name"
          type="text"
          name="name"
          placeholder="Name"
        />
      )}
      <TextField
        label="Email"
        type="text"
        name="email"
        placeholder="Email"
      />
      <TextField
        label="Password"
        type="password"
        name="password"
        placeholder="Password"
      />
      <div className={classes.actions}>
        <Button type="submit"> {isLogin ? "Login" : "Create Account"} </Button>
        <Button
          // 改为普通按钮类型,避免触发默认重置/提交行为
          type="button"
          className="toggle"
          onClick={() => { 
            setIsLogin((prevState) => !prevState);
            // 切换模式时清空name字段的值、touched状态、校验错误
            formik.setFieldValue('name', '');
            formik.setFieldTouched('name', false);
            formik.setFieldError('name', undefined);
          }}
        >
          {isLogin ? "Create new account" : "Login with existing account"}
        </Button>
      </div>
    </Form>
  )}
</Formik>
补充说明

如果你一定要保留hideName字段存在Formik values中的写法,不需要改校验schema,只需要在切换isLogin的时候,手动调用formik.setFieldValue('hideName', !isLogin)同步Formik内部的hideName值即可,同时也要记得清理name字段的残留状态。
不建议直接给Formik加enableReinitialize={true}实现状态同步,这个属性会在initialValues变化时重置整个表单,用户已经输入的邮箱、密码内容会被清空,体验很差。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 12:03:17