React From表单使用Formik和yup进行校验

发布于:2025-09-10 ⋅ 阅读:(23) ⋅ 点赞:(0)

一、Formik的使用

官方文档地址:https://formik.org/docs/tutorial#validation

  1. 首先安装依赖
yarn add  formik

2.导入并初始化

import { useFormik } from 'formik';
initialValues:初始化 输入框的密码和账号 
onSubmit:当点击提交按钮时,调用这个钩子,拿到输入框的vaule值

 3.打印一下formik看一下都有哪些钩子

 const formik = useFormik({
        initialValues: {
            mobile: '',
            code: ''
        },
        validate,
        onSubmit: values => {
            // 拿到输入框的值
            console.log(values);
        },
    });
    console.log(formik);

 

4.在form表单中绑定这些钩子会自动调用

5.formik里提供了校验规则,但是还是要自己手动写一下

6.进行校验结果控制

{formik.touched.mobile && formik.errors.mobile ? < div className='validate'>{formik.errors.mobile}</div> : null}

 完整使用

import React from 'react'
import NavBar from '../NavBar/NavBar'
import style from './Login.module.scss'
import Input from '../Input/input.js'
// 导入表单验证的formik
import { useFormik } from 'formik';
//导入校验验证规则yup
//import * as yup from 'yup';
const validate = values => {
    const error = {}
    if (!values.mobile) {
        error.mobile = '手机号不能为空'
    }
    if (!values.code) {
        error.code = '验证码不能为空'
    }
    return error
}
export default function Login() {
    const formik = useFormik({
        initialValues: {
            mobile: '',
            code: ''
        },
        validate,
        onSubmit: values => {
            // 拿到输入框的值
            console.log(values);
        },
    });
    console.log(formik);
    return (
        <div className={style.root}>
            <NavBar>登录</NavBar>
            <div className='content'>
                <h3>短信登录</h3>
                <form onSubmit={formik.handleSubmit}>
                    <div className='input-item'>
                        <Input
                            name='mobile'
                            placeholder='请输入手机号'
                            value={formik.values.mobile}
                            onChange={formik.handleChange}
                            onBlur={formik.handleBlur}
                        />
                        {formik.touched.mobile && formik.errors.mobile ? < div className='validate'>{formik.errors.mobile}</div> : null}
 
                        <div className='input-item'>
                            <Input
                                placeholder='请输入验证码'
                                extra='获取验证码'
                                name='code'
                                onChange={formik.handleChange}
                                onBlur={formik.handleBlur}
                                value={formik.values.code} />
                            {formik.touched.code && formik.errorscode ? <div className='validate'>{formik.errors.code}</div> : null}
                        </div>
                        <button type='submit' className='login-btn'>登录</button>
                    </div>
                </form>
            </div >
        </div >
    )
}

二、使用yup进行校验

第一部分的校验看起来不是很方便,但是如果使用yup进行校验的话会比较方便一些

yup文档:https://www.npmjs.com/package/yup

三、实战过程及讲解

工具 职责
Formik 表单状态管家(值、错误、是否通过)

Yup

校验规则书写器(字段必须满足什么条件)
useValldate 自定义钩子,把错误信息转换为formik能用的格式
  1. 把validate函数注册给formik函数
<Formik validate={validate} ... />
validate(currentFormValues)   // 当前整个表单值
import { useCallback, useMemo } from 'react';
import { catchYupError } from '@tencent/dboss-module-utility/common/utils/yup/catchYupError';
import * as Yup from 'yup';
import { set } from 'lodash';
import { t } from '@i18n';
import { validationSchema as backupStorageValidationSchema } from '@tencent/dboss-module-dbs/components/BackupStorageConfigForm';
import { StepId } from '../constants';
import { CrossClusterRollbackFormValues } from '../types';

const baseValidationSchemaMap = {
  sourceInstanceInfo: Yup.object({
    SourceInstanceId: Yup.string().required(t('该项为必填项')),
    StorageConfig: backupStorageValidationSchema({ allNotRequired: false }),
  }),
  // 其他步骤的校验逻辑
};

export const useValidate = (stepId: StepId) => {
  const validationSchema = useMemo(() => {
    const schema = baseValidationSchemaMap[stepId];

    if (typeof schema === 'function') {
      return schema();
    }
    return schema;
  }, [stepId]);

  const formValidator = useCallback(
    (formData: CrossClusterRollbackFormValues) => {
      const errors = {};
      if (validationSchema) {
        const schemaErrors = catchYupError(formData, validationSchema) ?? [];
        schemaErrors.forEach(({ path, message }) => {
          path && set(errors, path, message);
        });
      }
      switch (stepId) {
        case 'sourceInstanceInfo':
          // 移除这部分代码,因为 StorageConfig 的校验已经在 baseValidationSchemaMap 中处理了
          break;
        case 'rollbackBasicSettings':
          catchYupError(
            formData,
            Yup.object({
              PhysicalRollbackConfig: Yup.object({
                RollbackTime: Yup.string().required(t('该项为必填项')),
              }),
            }),
          ).forEach(({ path, message }) => {
            path && set(errors, path, message);
          });
          break;
        case 'targetInstanceSettings':
          catchYupError(
            formData,
            Yup.object({
              InstanceName: Yup.string().required(t('该项为必填项')),
            }),
          ).forEach(({ path, message }) => {
            path && set(errors, path, message);
          });
          break;
        case 'confirmation':
          break;
      }
      return errors;
    },
    [stepId, validationSchema],
  );

  return formValidator;
};

 

步骤 规则(yup对象)
SourceInstanceInfo SourceInstanceInfo+StorageConfig
rollbackBasicSettings PhysicalRollbackConfig.RollbackTime
targetInstanceSettings InstanceName

4.执行yup校验(以sourceInstanceInfo为例子)

Yup.object({
  SourceInstanceId: Yup.string().required('该项为必填项'),
  StorageConfig: backupStorageValidationSchema({ allNotRequired: false }),
})

backupstoragevaldationSchema会生成:

Yup.object({
  BackupDir: Yup.string().required('该项为必填项'),
  Bucket: Yup.string().required('该项为必填项'),
  L5ServerName: Yup.string().required('该项为必填项'),
  Endpoint: Yup.string().required('该项为必填项'),
  AK: Yup.string().required('该项为必填项'),
  SK: Yup.string().required('该项为必填项'),
});

 5.catchYupError会将错误信息转换为数组

[
  { path: 'SourceInstanceId', message: '该项为必填项' },
  { path: 'StorageConfig.BackupDir', message: '该项为必填项' },
  { path: 'StorageConfig.Bucket', message: '该项为必填项' },
  ...
]

 6.组装成Formik可以用的errors对象

{
  SourceInstanceId: '该项为必填项',
  StorageConfig: {
    BackupDir: '该项为必填项',
    Bucket: '该项为必填项',
    ...
  }
}

 7.formik收到errors

  • 如果 空对象 → 校验通过,立即执行 onSubmit
  • 如果 有字段 → 校验失败,阻断提交,并在对应 <InputField> 下显示红色提示

四、常见踩坑

  1. 字段路径写错
    例如把 StorageConfig.Bucket 写成 Bucket → Yup 找不到字段,永远通过。
  2. initialValues 缺字段
    缺 StorageType → Yup 认为它是 undefined,不会触发 required()
  3. allNotRequired: true 传错
    传了 true → Yup 内部  加 .required(),永远通过。
  4. catchYupError 返回空数组
    说明 Yup 侧已经通过,问题一定在 路径或初始值
一句话总结:
「Formik 负责喊『校验!』,Yup 负责喊『哪里错了!』,useValidate 负责把错误翻译成 Formik 能看懂的地图,地图为空就放行 onSubmit。」

网站公告

今日签到

点亮在社区的每一天
去签到