驗證

Formik 的設計旨在輕鬆管理具有複雜驗證的表單。Formik 支援同步和非同步的表單級和欄位級驗證。此外,它還內建支援透過 Yup 進行基於 Schema 的表單級驗證。本指南將詳細說明以上所有內容。

驗證的類型

表單級驗證

表單級驗證很有用,因為您可以在函式執行時完全存取所有表單的 values 和屬性,因此您可以同時驗證相依欄位。

有兩種方法可以使用 Formik 進行表單級驗證

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

validate

<Formik>withFormik() 接受一個名為 validate 的屬性/選項,它接受同步或非同步函式。

// 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

如您在上面看到的,驗證由您決定。您可以自行編寫驗證器或使用第三方函式庫。在 The Palmer Group,我們使用 Yup 進行物件 Schema 驗證。它有一個與 JoiReact PropTypes 非常相似的 API,但它足夠小,可以在瀏覽器中使用,並且速度足夠快,可以在執行時使用。因為我們 ❤️ Yup 非常多,所以 Formik 有一個針對 Yup 物件 Schema 的特殊設定選項/屬性,稱為 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> 元件的 validate 屬性或 useField hook 進行欄位級驗證。此函式可以是同步或非同步的(返回 Promise)。預設情況下,它會在任何 onChangeonBlur 之後執行。可以使用頂層 <Formik/> 元件中的 validateOnChangevalidateOnBlur 屬性分別更改此行為。除了更改/模糊之外,所有欄位級驗證都會在提交嘗試開始時執行,然後將結果與任何頂層驗證結果深度合併。

注意:<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> 屬性的值來控制 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>
<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 屬性為 true

我可以傳回 `null` 作為錯誤訊息嗎?

不行。請改用 undefined。Formik 使用 undefined 來表示空狀態。如果您使用 null,Formik 的幾個計算屬性部分(例如 isValid)將無法按預期工作。

如何測試驗證?

Formik 針對 Yup 驗證已具備廣泛的單元測試,因此您不需要再自行測試。然而,如果您自行撰寫驗證函式,則應對其進行單元測試。如果您需要測試 Formik 的執行流程,則應分別使用指令式方法 validateFormvalidateField

這個頁面有幫助嗎?

訂閱我們的電子報

最新的 Formik 消息、文章和資源,將會寄送到您的收件箱。

版權所有 © 2020 Formium, Inc. 保留一切權利。