一、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
。」