如何基于React状态值使用Yup实现条件验证?
解决Formik+Yup结合React状态校验用户名是否被占用的问题
核心思路
放弃用Yup的when依赖表单内部字段,改用test方法直接读取外部React状态,同时监听用户名输入调用后端接口更新状态,让Formik感知状态变化后重新验证。
具体实现步骤
- 初始化React状态存储用户名占用状态
import { useState, useEffect } from 'react'; import { Formik } from 'formik'; import * as yup from 'yup'; const SignupForm = () => { // isUsernameTaken为true时,表示用户名已被占用(对应后端返回false) const [isUsernameTaken, setIsUsernameTaken] = useState(false);
- 修改Yup校验规则,用test方法关联外部状态
const validationSchema = yup.object({ username: yup.string() .required('用户名不能为空') .test( 'username-not-taken', // 校验唯一标识 '该用户名已被占用', // 错误提示文本 () => !isUsernameTaken // 校验逻辑:状态为true时,校验失败,显示错误 ), password: yup.string().required('密码不能为空'), confirmPassword: yup.string() .required('请确认密码') .oneOf([yup.ref('password'), null], '两次密码输入不一致') });
- 监听用户名输入,调用后端接口更新状态
return ( <Formik initialValues={{ username: '', password: '', confirmPassword: '' }} validationSchema={validationSchema} enableReinitialize // 开启该属性,让Formik感知外部状态变化后重新验证 onSubmit={(values) => { // 表单提交逻辑 console.log('提交数据:', values); }} > {({ values, handleChange, handleSubmit, errors, touched }) => { // 监听用户名输入变化,调用后端接口 useEffect(() => { const checkUsername = async () => { if (!values.username.trim()) { setIsUsernameTaken(false); return; } try { const res = await fetch(`/api/check-username?username=${values.username}`); const data = await res.json(); // 后端返回false表示用户名已存在,所以设置状态为true setIsUsernameTaken(!data); } catch (err) { console.error('校验用户名失败:', err); } }; checkUsername(); }, [values.username]); return ( <form onSubmit={handleSubmit}> {/* 用户名输入框 */} <div> <label>用户名</label> <input type="text" name="username" value={values.username} onChange={handleChange} /> {touched.username && errors.username && ( <span style={{ color: 'red', marginLeft: '8px' }}>{errors.username}</span> )} </div> {/* 密码输入框 */} <div style={{ marginTop: '16px' }}> <label>密码</label> <input type="password" name="password" value={values.password} onChange={handleChange} /> {touched.password && errors.password && ( <span style={{ color: 'red', marginLeft: '8px' }}>{errors.password}</span> )} </div> {/* 确认密码输入框 */} <div style={{ marginTop: '16px' }}> <label>确认密码</label> <input type="password" name="confirmPassword" value={values.confirmPassword} onChange={handleChange} /> {touched.confirmPassword && errors.confirmPassword && ( <span style={{ color: 'red', marginLeft: '8px' }}>{errors.confirmPassword}</span> )} </div> <button type="submit" style={{ marginTop: '16px' }}>注册</button> </form> ); }} </Formik> ); }; export default SignupForm;
关键说明
- 用Yup的
test方法替代when,直接读取外部React状态,避开了when只能依赖表单内部字段的限制。 - 通过
enableReinitialize让Formik在外部状态变化时自动重新执行校验,确保错误提示能实时更新。 - 后端返回逻辑对应:后端返回
false表示用户名已存在,因此我们将isUsernameTaken设为true,触发校验失败并显示错误。
内容的提问来源于stack exchange,提问作者user19643485
相关产品推荐
相关产品推荐

