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

如何基于React状态值使用Yup实现条件验证?

解决Formik+Yup结合React状态校验用户名是否被占用的问题

核心思路

放弃用Yup的when依赖表单内部字段,改用test方法直接读取外部React状态,同时监听用户名输入调用后端接口更新状态,让Formik感知状态变化后重新验证。

具体实现步骤

  1. 初始化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);
  1. 修改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], '两次密码输入不一致')
  });
  1. 监听用户名输入,调用后端接口更新状态
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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 18:15:27