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

