React From表单使用Formik和yup进行校验
一、Formik的使用
官方文档地址:https://formik.org/docs/tutorial#validation
- 首先安装依赖
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'><Inputname='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'><Inputplaceholder='请输入验证码'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能用的格式 |
- 把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>
下显示红色提示
四、常见踩坑
- 字段路径写错
例如把StorageConfig.Bucket
写成Bucket
→ Yup 找不到字段,永远通过。 - initialValues 缺字段
缺StorageType
→ Yup 认为它是undefined
,不会触发required()
。 -
allNotRequired: true
传错
传了true
→ Yup 内部 不 加.required()
,永远通过。 -
catchYupError
返回空数组
说明 Yup 侧已经通过,问题一定在 路径或初始值
「Formik 负责喊『校验!』,Yup 负责喊『哪里错了!』,
useValidate
负责把错误翻译成 Formik 能看懂的地图,地图为空就放行 onSubmit
。」