验证

Formik 旨在轻松管理具有复杂验证的表单。Formik 支持同步和异步的表单级和字段级验证。此外,它还内置支持通过 Yup 进行基于模式的表单级验证。本指南将介绍上述所有内容的来龙去脉。

验证类型

表单级验证

表单级验证很有用,因为无论何时运行该函数,您都可以完全访问表单的所有 values 和 props,因此您可以同时验证依赖字段。

Formik 有两种方法可以进行表单级验证

  • <Formik validate>withFormik({ validate: ... })
  • <Formik validationSchema>withFormik({ validationSchema: ... })

validate

<Formik>withFormik() 接受一个名为 validate 的 prop/选项,该选项接受同步或异步函数。

// Synchronous validation
const validate = (values, props /* only available when using withFormik */) => {
const errors = {};
if (!values.email) {
errors.email = 'Required';
} else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email)) {
errors.email = 'Invalid email address';
}
//...
return errors;
};
// Async Validation
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
const validate = (values, props /* only available when using withFormik */) => {
return sleep(2000).then(() => {
const errors = {};
if (['admin', 'null', 'god'].includes(values.username)) {
errors.username = 'Nice try';
}
// ...
return errors;
});
};

有关 <Formik validate> 的更多信息,请参阅 API 参考。

validationSchema

如上所示,验证由您自己决定。您可以随意编写自己的验证器或使用第三方库。在 Palmer 集团,我们使用 Yup 进行对象模式验证。它具有与 JoiReact PropTypes 非常相似的 API,但体积足够小,可以在浏览器中使用,并且速度足够快,可以在运行时使用。因为我们非常喜欢 Yup,所以 Formik 有一个特殊的配置选项/prop 用于 Yup 对象模式,称为 validationSchema,它会自动将 Yup 的验证错误转换为一个漂亮的对象,其键与 valuestouched 匹配。这种对称性使管理错误消息周围的业务逻辑变得容易。

要将 Yup 添加到您的项目中,请从 NPM 安装它。

npm install yup --save
import React from 'react';
import { Formik, Form, Field } from 'formik';
import * as Yup from 'yup';
const SignupSchema = Yup.object().shape({
firstName: Yup.string()
.min(2, 'Too Short!')
.max(50, 'Too Long!')
.required('Required'),
lastName: Yup.string()
.min(2, 'Too Short!')
.max(50, 'Too Long!')
.required('Required'),
email: Yup.string().email('Invalid email').required('Required'),
});
export const ValidationSchemaExample = () => (
<div>
<h1>Signup</h1>
<Formik
initialValues={{
firstName: '',
lastName: '',
email: '',
}}
validationSchema={SignupSchema}
onSubmit={values => {
// same shape as initial values
console.log(values);
}}
>
{({ errors, touched }) => (
<Form>
<Field name="firstName" />
{errors.firstName && touched.firstName ? (
<div>{errors.firstName}</div>
) : null}
<Field name="lastName" />
{errors.lastName && touched.lastName ? (
<div>{errors.lastName}</div>
) : null}
<Field name="email" type="email" />
{errors.email && touched.email ? <div>{errors.email}</div> : null}
<button type="submit">Submit</button>
</Form>
)}
</Formik>
</div>
);

有关 <Formik validationSchema> 的更多信息,请参阅 API 参考。

字段级验证

validate

Formik 通过 <Field>/<FastField> 组件或 useField 钩子的 validate prop 支持字段级验证。此函数可以是同步的或异步的(返回 Promise)。默认情况下,它将在任何 onChangeonBlur 之后运行。此行为可以在顶级 <Formik/> 组件中分别使用 validateOnChangevalidateOnBlur props 进行更改。除了更改/失焦之外,所有字段级验证都将在提交尝试开始时运行,然后结果与任何顶级验证结果深度合并。

注意:<Field>/<FastField> 组件的 validate 函数仅在已挂载的字段上执行。也就是说,如果您的任何字段在表单流程期间卸载(例如,Material-UI 的 <Tabs> 卸载了用户所在的先前 <Tab>),则这些字段在表单验证/提交期间不会被验证。

import React from 'react';
import { Formik, Form, Field } from 'formik';
function validateEmail(value) {
let error;
if (!value) {
error = 'Required';
} else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(value)) {
error = 'Invalid email address';
}
return error;
}
function validateUsername(value) {
let error;
if (value === 'admin') {
error = 'Nice try!';
}
return error;
}
export const FieldLevelValidationExample = () => (
<div>
<h1>Signup</h1>
<Formik
initialValues={{
username: '',
email: '',
}}
onSubmit={values => {
// same shape as initial values
console.log(values);
}}
>
{({ errors, touched, isValidating }) => (
<Form>
<Field name="email" validate={validateEmail} />
{errors.email && touched.email && <div>{errors.email}</div>}
<Field name="username" validate={validateUsername} />
{errors.username && touched.username && <div>{errors.username}</div>}
<button type="submit">Submit</button>
</Form>
)}
</Formik>
</div>
);

手动触发验证

您可以使用 Formik 的 validateFormvalidateField 方法分别手动触发表单级和字段级验证。

import React from 'react';
import { Formik, Form, Field } from 'formik';
function validateEmail(value) {
let error;
if (!value) {
error = 'Required';
} else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(value)) {
error = 'Invalid email address';
}
return error;
}
function validateUsername(value) {
let error;
if (value === 'admin') {
error = 'Nice try!';
}
return error;
}
export const FieldLevelValidationExample = () => (
<div>
<h1>Signup</h1>
<Formik
initialValues={{
username: '',
email: '',
}}
onSubmit={values => {
// same shape as initial values
console.log(values);
}}
>
{({ errors, touched, validateField, validateForm }) => (
<Form>
<Field name="email" validate={validateEmail} />
{errors.email && touched.email && <div>{errors.email}</div>}
<Field name="username" validate={validateUsername} />
{errors.username && touched.username && <div>{errors.username}</div>}
{/** Trigger field-level validation
imperatively */}
<button type="button" onClick={() => validateField('username')}>
Check Username
</button>
{/** Trigger form-level validation
imperatively */}
<button
type="button"
onClick={() => validateForm().then(() => console.log('blah'))}
>
Validate All
</button>
<button type="submit">Submit</button>
</Form>
)}
</Formik>
</div>
);

何时运行验证?

您可以通过更改 <Formik validateOnChange> 和/或 <Formik validateOnBlur> props 的值来控制 Formik 何时运行验证,具体取决于您的需求。默认情况下,Formik 将按如下方式运行验证方法

在“更改”事件/方法之后(更新 values 的内容)

  • handleChange
  • setFieldValue
  • setValues

在“失焦”事件/方法之后(更新 touched 的内容)

  • handleBlur
  • setTouched
  • setFieldTouched

在尝试提交时

  • handleSubmit
  • submitForm

Formik 的渲染/注入 props 还提供了命令式帮助器方法,您可以使用这些方法以命令式方式调用验证。

  • validateForm
  • validateField

显示错误消息

错误消息取决于表单的验证。如果存在错误,并且验证函数生成了一个错误对象(应该如此),并且其形状与我们的 values/initialValues 匹配,则可以从 errors 对象访问依赖字段错误。

import React from 'react';
import { Formik, Form, Field } from 'formik';
import * as Yup from 'yup';
const DisplayingErrorMessagesSchema = Yup.object().shape({
username: Yup.string()
.min(2, 'Too Short!')
.max(50, 'Too Long!')
.required('Required'),
email: Yup.string().email('Invalid email').required('Required'),
});
export const DisplayingErrorMessagesExample = () => (
<div>
<h1>Displaying Error Messages</h1>
<Formik
initialValues={{
username: '',
email: '',
}}
validationSchema={DisplayingErrorMessagesSchema}
onSubmit={values => {
// same shape as initial values
console.log(values);
}}
>
{({ errors, touched }) => (
<Form>
<Field name="username" />
{/* If this field has been touched, and it contains an error, display it
*/}
{touched.username && errors.username && <div>{errors.username}</div>}
<Field name="email" />
{/* If this field has been touched, and it contains an error, display
it */}
{touched.email && errors.email && <div>{errors.email}</div>}
<button type="submit">Submit</button>
</Form>
)}
</Formik>
</div>
);

ErrorMessage 组件也可用于显示错误消息。

常见问题

如何确定我的表单是否正在验证?

如果 isValidating prop 为 true

我可以返回 `null` 作为错误消息吗?

不可以。请改用 undefined。Formik 使用 undefined 来表示空状态。如果您使用 null,Formik 的一些计算 prop(例如 isValid)将无法按预期工作。

如何测试验证?

Formik 对 Yup 验证进行了广泛的单元测试,因此您无需测试它。但是,如果您正在编写自己的验证函数,则只需对这些函数进行单元测试即可。如果您确实需要测试 Formik 的执行,则应分别使用命令式 validateFormvalidateField 方法。

此页面是否有帮助?

订阅我们的时事通讯

最新的 Formik 新闻、文章和资源,发送到您的收件箱。

版权所有 © 2020 Formium, Inc. 保留所有权利。