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

一、Formik的使用

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

  1. 首先安装依赖
TypeScript 复制代码
yarn add  formik

2.导入并初始化

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

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

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

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

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

6.进行校验结果控制

TypeScript 复制代码
{formik.touched.mobile && formik.errors.mobile ? < div className='validate'>{formik.errors.mobile}</div> : null}

完整使用

TypeScript 复制代码
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函数
TypeScript 复制代码
<Formik validate={validate} ... />
TypeScript 复制代码
validate(currentFormValues)   // 当前整个表单值
TypeScript 复制代码
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为例子)

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

backupstoragevaldationSchema会生成:

TypeScript 复制代码
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会将错误信息转换为数组

TypeScript 复制代码
[
  { path: 'SourceInstanceId', message: '该项为必填项' },
  { path: 'StorageConfig.BackupDir', message: '该项为必填项' },
  { path: 'StorageConfig.Bucket', message: '该项为必填项' },
  ...
]

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

TypeScript 复制代码
{
  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。」

相关推荐
应用市场4 小时前
构建自定义命令行工具 - 打造专属指令体
开发语言·windows·python
岁月宁静4 小时前
深度定制:在 Vue 3.5 应用中集成流式 AI 写作助手的实践
前端·vue.js·人工智能
Dfreedom.4 小时前
一文掌握Python四大核心数据结构:变量、结构体、类与枚举
开发语言·数据结构·python·变量·数据类型
一半烟火以谋生4 小时前
Python + Pytest + Allure 自动化测试报告教程
开发语言·python·pytest
虚行4 小时前
C#上位机工程师技能清单文档
开发语言·c#
小羊在睡觉5 小时前
golang定时器
开发语言·后端·golang
心易行者5 小时前
10天!前端用coze,后端用Trae IDE+Claude Code从0开始构建到平台上线
前端
CoderCodingNo5 小时前
【GESP】C++四级真题 luogu-B4068 [GESP202412 四级] Recamán
开发语言·c++·算法
saadiya~5 小时前
ECharts 实时数据平滑更新实践(含 WebSocket 模拟)
前端·javascript·echarts
fruge5 小时前
前端三驾马车(HTML/CSS/JS)核心概念深度解析
前端·css·html