Skip to main content

Formik Validation With Yup

Formik 内置属性validationSchema可以无缝地使用 yup 来做 validation 验证,比如:

<Formik
initialValues={{ user: '', password: '' }}
onSubmit={(values) => {
console.log(values.user, values.password);
}}
validationSchema={yup.object({
user: yup.string().required(),
password: yup.string().min(8).required(),
})}
>
<input id="user" />
<input id="password" />
</Formik>

高亮部分就是简单的针对userpassword字段的校验,如果校验失败,那么就可以通过 formik 的errors字段获取到报错信息。

常用场景

检查数组的元素

这里并不是简单的去检查数组有多少个元素,而是检查数组中的对象是否满足条件,比如检查一个数组数据:

const data = {
links: [
{
link: 'https://www.google.com',
},
{
link: 'google',
},
],
};

期望验证数组元素中的link字段需要是合法的 url。这里就需要用到yup.array().of方法:

const schema = yup.object().shape({
links: yup.array().of(
yup.object().shape({
link: yup
.string()
.required()
.test('test', (value, context) => {
return isLink(value);
}),
})
),
});

上面代码中,高亮的value值就是数据元素中的link字段了

根据数据中的其他字段,做不同的校验 (Conditional Validation Based on Other Fields)

简单的来说,就是有些字段会有相互约束,这就用到了 yup 的when语法, 比如有一个数据结构如下:

const LoginData = {
name: string;
// 'password' | 'oauth'
loginType: string;
password?:string;
}

如果 loginTypepassword,那么就验证 password 是否合法。

const schema = yup.object().shape({
password: yup.string().when('loginType', {
is: (value: string) => {
return value === 'password';
},
then: (rules) => {
// password is required and at least 8 letters
return rules.required().min(8);
},
otherwise: yup.string().notRequired(),
}),
});

跨字段级别的验证

比如我们 2 个字段是 NotRequired,但是其中一个必须有值。

const validationSchema = yup
.object()
.shape({
fieldA: yup.string(),
fieldB: yup.string(),
})
.test(
'either-field',
'At least one of Field A or Field B must be filled',
(value) => {
const { fieldA, fieldB } = value;
return !!(fieldA || fieldB);
}
);

自定义 error

有时候我们会根据条件来返回不同的 error,比如 password 我们需要根据以下几个场景来区分报错信息:

  1. 必须包含特定的字符
  2. 不能包含用户名的信息

为了区分报错信息,就需要用 yup 中的 createError 方法:

yup.string().test('check password', (value, { createError, path, parent }) => {
if (!hasSpecialSymbols(value)) {
return createError({
path,
message: 'password should include special symbols',
});
}
// other ...
return true;
});

异步验证

yup 的 test 方法传递的函数可以是异步函数,这样可以实现需要 fetch 请求的验证。

const validationSchema = yup.object().shape({
email: yup
.string()
.email('Invalid email format')
.required('Email is required')
.test('checkEmailExists', 'Email already exists', async (value) => {
const response = await fetch(`/api/check-email?email=${value}`);
const data = await response.json();
return !data.exists; // Assuming the API returns { exists: true/false }
}),
});

type 约束

针对 TypeScript,可以采用以下类型约束,这样在定义 schema 的时候可以防止会有字段名写错

import type { AnySchema } from 'yup';
import * as yup from 'yup';

type Shaped<T> = Record<keyof T, AnySchema>;

export const yupShape = <T>(shape: Partial<Shaped<T>>) =>
yup.object().shape(shape as Shaped<T>);

使用的时候将需要验证的数据类型传入:

const schema = yupShape<LoginData>({
//...
});