验证

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

¥Formik is designed to manage forms with complex validation with ease. Formik supports synchronous and asynchronous form-level and field-level validation. Furthermore, it comes with baked-in support for schema-based form-level validation through Yup. This guide will describe the ins and outs of all of the above.

验证的风格

¥Flavors of Validation

表单级验证

¥Form-level Validation

表单级验证非常有用,因为只要函数运行,你就可以完全访问表单的所有 values 和 props,因此你可以同时验证依赖字段。

¥Form-level validation is useful because you have complete access to all of your form's values and props whenever the function runs, so you can validate dependent fields at the same time.

使用 Formik 进行表单级验证有两种方法:

¥There are 2 ways to do form-level validation with Formik:

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

    ¥<Formik validate> and withFormik({ validate: ... })

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

    ¥<Formik validationSchema> and withFormik({ validationSchema: ... })

validate

<Formik>withFormik() 采用名为 validate 的属性/选项,它接受同步或异步函数。

¥<Formik> and withFormik() take a prop/option called validate that accepts either a synchronous or asynchronous function.

// 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 参考。

¥For more information about <Formik validate>, see the API reference.

validationSchema

正如你在上面看到的,验证由你决定。你可以随意编写自己的验证器或使用第三方库。在 Palmer Group,我们使用 是的 进行对象模式验证。它有一个与 JoiReact PropTypes 非常相似的 API,但对于浏览器来说足够小,对于运行时使用来说足够快。因为我们❤️ Yup 太多了,Formik 有一个名为 validationSchema 的 Yup 对象模式的特殊配置选项/属性,它会自动将 Yup 的验证错误转换为一个漂亮的对象,其键与 valuestouched 匹配。这种对称性使得管理围绕错误消息的业务逻辑变得容易。

¥As you can see above, validation is left up to you. Feel free to write your own validators or use a 3rd party library. At The Palmer Group, we use Yup for object schema validation. It has an API that's pretty similar to Joi and React PropTypes but is small enough for the browser and fast enough for runtime usage. Because we ❤️ Yup sooo much, Formik has a special config option / prop for Yup object schemas called validationSchema which will automatically transform Yup's validation errors into a pretty object whose keys match values and touched. This symmetry makes it easy to manage business logic around error messages.

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

¥To add Yup to your project, install it from 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 参考。

¥For more information about <Formik validationSchema>, see the API reference.

字段级验证

¥Field-level Validation

validate

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

¥Formik supports field-level validation via the validate prop of <Field>/<FastField> components or useField hook. This function can be synchronous or asynchronous (return a Promise). It will run after any onChange and onBlur by default. This behavior can be altered at the top level <Formik/> component using the validateOnChange and validateOnBlur props respectively. In addition to change/blur, all field-level validations are run at the beginning of a submission attempt and then the results are deeply merged with any top-level validation results.

注意:<Field>/<FastField> 组件的 validate 功能将仅在已安装的字段上执行。也就是说,如果你的任何字段在表单流程期间卸载(例如,Material-UI 的 <Tabs> 卸载了用户之前使用的 <Tab>),则在表单验证/提交期间将不会验证这些字段。

¥Note: The <Field>/<FastField> components' validate function will only be executed on mounted fields. That is to say, if any of your fields unmount during the flow of your form (e.g. Material-UI's <Tabs> unmounts the previous <Tab> your user was on), those fields will not be validated during form validation/submission.

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>
);

手动触发验证

¥Manually Triggering Validation

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

¥You can manually trigger both form-level and field-level validation with Formik using the validateForm and validateField methods respectively.

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>
);

验证何时运行?

¥When Does Validation Run?

你可以根据需要更改 <Formik validateOnChange> 和/或 <Formik validateOnBlur> 属性的值来控制 Formik 何时运行验证。默认情况下,Formik 将运行验证方法,如下所示:

¥You can control when Formik runs validation by changing the values of <Formik validateOnChange> and/or <Formik validateOnBlur> props depending on your needs. By default, Formik will run validation methods as follows:

在 "change" 事件/方法之后(更新 values 的事情)

¥After "change" events/methods (things that updatevalues)

  • handleChange

  • setFieldValue

  • setValues

"blur" 事件/方法之后(更新 touched 的事情)

¥After "blur" events/methods (things that update touched)

  • handleBlur

  • setTouched

  • setFieldTouched

每当尝试提交时

¥Whenever submission is attempted

  • handleSubmit

  • submitForm

还通过 Formik 的 render/injected props 为你提供了命令式帮助方法,你可以使用它们来命令式调用验证。

¥There are also imperative helper methods provided to you via Formik's render/injected props which you can use to imperatively call validation.

  • validateForm

  • validateField

显示错误消息

¥Displaying Error Messages

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

¥Error messages are dependent on the form's validation. If an error exists, and the validation function produces an error object (as it should) with a matching shape to our values/initialValues, dependent field errors can be accessed from the errors object.

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 组件还可用于显示错误消息。

¥The ErrorMessage component can also be used to display error messages.

常见问题

¥Frequently Asked Questions

How do I determine if my form is validating?

如果 isValidating 属性是 true

¥If isValidating prop is true

Can I return `null` as an error message?

不。请改用 undefined。Formik 使用 undefined 来表示空状态。如果你使用 null,Formik 的计算属性的几个部分(例如 isValid)将无法按预期工作。

¥No. Use undefined instead. Formik uses undefined to represent empty states. If you use null, several parts of Formik's computed props (e.g. isValid for example), will not work as expected.

How do I test validation?

Formik 有大量用于 Yup 验证的单元测试,因此你无需对其进行测试。但是,如果你正在滚动自己的验证函数,则应该简单地对它们进行单元测试。如果你确实需要测试 Formik 的执行情况,你应该分别使用命令式 validateFormvalidateField 方法。

¥Formik has extensive unit tests for Yup validation so you do not need to test that. However, if you are rolling your own validation functions, you should simply unit test those. If you do need to test Formik's execution you should use the imperative validateForm and validateField methods respectively.

Formik 中文网 - 粤ICP备13048890号