Formik 的設計旨在輕鬆管理具有複雜驗證的表單。Formik 支援同步和非同步的表單級和欄位級驗證。此外,它還內建支援透過 Yup 進行基於 Schema 的表單級驗證。本指南將詳細說明以上所有內容。
表單級驗證很有用,因為您可以在函式執行時完全存取所有表單的 values
和屬性,因此您可以同時驗證相依欄位。
有兩種方法可以使用 Formik 進行表單級驗證
<Formik validate>
和 withFormik({ validate: ... })
<Formik validationSchema>
和 withFormik({ validationSchema: ... })
validate
<Formik>
和 withFormik()
接受一個名為 validate
的屬性/選項,它接受同步或非同步函式。
// 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
如您在上面看到的,驗證由您決定。您可以自行編寫驗證器或使用第三方函式庫。在 The Palmer Group,我們使用 Yup 進行物件 Schema 驗證。它有一個與 Joi 和 React PropTypes 非常相似的 API,但它足夠小,可以在瀏覽器中使用,並且速度足夠快,可以在執行時使用。因為我們 ❤️ Yup 非常多,所以 Formik 有一個針對 Yup 物件 Schema 的特殊設定選項/屬性,稱為 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>
元件的 validate
屬性或 useField
hook 進行欄位級驗證。此函式可以是同步或非同步的(返回 Promise)。預設情況下,它會在任何 onChange
和 onBlur
之後執行。可以使用頂層 <Formik/>
元件中的 validateOnChange
和 validateOnBlur
屬性分別更改此行為。除了更改/模糊之外,所有欄位級驗證都會在提交嘗試開始時執行,然後將結果與任何頂層驗證結果深度合併。
注意:
<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>
屬性的值來控制 Formik 何時執行驗證。預設情況下,Formik 將按如下方式執行驗證方法
**在「更改」事件/方法之後**(更新 values
的操作)
handleChange
setFieldValue
setValues
**在「模糊」事件/方法之後**(更新 touched
的操作)
handleBlur
setTouched
setFieldTouched
每次嘗試提交時
handleSubmit
submitForm
Formik 的渲染/注入屬性也为您提供了一些命令式輔助方法,您可以使用它們以命令式方式呼叫驗證。
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
屬性為 true
不行。請改用 undefined
。Formik 使用 undefined
來表示空狀態。如果您使用 null
,Formik 的幾個計算屬性部分(例如 isValid
)將無法按預期工作。
Formik 針對 Yup 驗證已具備廣泛的單元測試,因此您不需要再自行測試。然而,如果您自行撰寫驗證函式,則應對其進行單元測試。如果您需要測試 Formik 的執行流程,則應分別使用指令式方法 validateForm
和 validateField
。