Formik 旨在轻松管理具有复杂验证的表单。Formik 支持同步和异步的表单级和字段级验证。此外,它还内置支持通过 Yup 进行基于模式的表单级验证。本指南将介绍上述所有内容的来龙去脉。
表单级验证很有用,因为无论何时运行该函数,您都可以完全访问表单的所有 values
和 props,因此您可以同时验证依赖字段。
Formik 有两种方法可以进行表单级验证
<Formik validate>
和 withFormik({ validate: ... })
<Formik validationSchema>
和 withFormik({ validationSchema: ... })
validate
<Formik>
和 withFormik()
接受一个名为 validate
的 prop/选项,该选项接受同步或异步函数。
// Synchronous validationconst 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 Validationconst 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 进行对象模式验证。它具有与 Joi 和 React PropTypes 非常相似的 API,但体积足够小,可以在浏览器中使用,并且速度足够快,可以在运行时使用。因为我们非常喜欢 Yup,所以 Formik 有一个特殊的配置选项/prop 用于 Yup 对象模式,称为 validationSchema
,它会自动将 Yup 的验证错误转换为一个漂亮的对象,其键与 values
和 touched
匹配。这种对称性使管理错误消息周围的业务逻辑变得容易。
要将 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><FormikinitialValues={{firstName: '',lastName: '',email: '',}}validationSchema={SignupSchema}onSubmit={values => {// same shape as initial valuesconsole.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)。默认情况下,它将在任何 onChange
和 onBlur
之后运行。此行为可以在顶级 <Formik/>
组件中分别使用 validateOnChange
和 validateOnBlur
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><FormikinitialValues={{username: '',email: '',}}onSubmit={values => {// same shape as initial valuesconsole.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 的 validateForm
和 validateField
方法分别手动触发表单级和字段级验证。
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><FormikinitialValues={{username: '',email: '',}}onSubmit={values => {// same shape as initial valuesconsole.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 validationimperatively */}<button type="button" onClick={() => validateField('username')}>Check Username</button>{/** Trigger form-level validationimperatively */}<buttontype="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><FormikinitialValues={{username: '',email: '',}}validationSchema={DisplayingErrorMessagesSchema}onSubmit={values => {// same shape as initial valuesconsole.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, displayit */}{touched.email && errors.email && <div>{errors.email}</div>}<button type="submit">Submit</button></Form>)}</Formik></div>);
ErrorMessage 组件也可用于显示错误消息。
如果 isValidating
prop 为 true
不可以。请改用 undefined
。Formik 使用 undefined
来表示空状态。如果您使用 null
,Formik 的一些计算 prop(例如 isValid
)将无法按预期工作。
Formik 对 Yup 验证进行了广泛的单元测试,因此您无需测试它。但是,如果您正在编写自己的验证函数,则只需对这些函数进行单元测试即可。如果您确实需要测试 Formik 的执行,则应分别使用命令式 validateForm
和 validateField
方法。