歡迎來到 Formik 教學。本教學將引導您學習在 React 中構建簡單和複雜表單所需的一切知識。
如果您迫不及待想在本地電腦上開始操作,請查看60 秒快速入門。
在本教學中,我們將使用 React 和 Formik 構建一個複雜的電子報註冊表單。
您可以在此處查看我們將構建的內容:最終結果。 如果您看不懂程式碼,別擔心!本教學的目標是幫助您理解 Formik。
您需要熟悉 HTML、CSS、現代 JavaScript 和 React(以及 React Hooks),才能完全理解 Formik 及其運作方式。在本教學中,我們將使用箭頭函式、let、const、展開語法、解構、計算屬性名稱 和 async/await。您可以使用 Babel REPL 來檢查 ES6 程式碼編譯後的結果。
完成本教學有兩種方法:您可以在瀏覽器中編寫程式碼,也可以在電腦上設定本地開發環境。
這是最快開始使用的方法!
首先,在新分頁中開啟此起始程式碼。新分頁應顯示一個電子郵件地址輸入欄位、一個提交按鈕和一些 React 程式碼。我們將在本教學中編輯 React 程式碼。
跳過第二個設定選項,直接前往概觀章節以了解 Formik 的概觀。
此步驟完全是可選的,本教學並不需要!
此設定需要更多工作,但允許您使用您選擇的編輯器來完成教學。以下是步驟
npx create-react-app my-app
npm i formik
或
yarn add formik
src/
資料夾中的所有檔案注意
**不要刪除整個
src
資料夾,只需刪除其中的原始程式碼檔案。**我們將在下一個步驟中用本專案的範例取代預設的程式碼檔案。
cd my-appcd src# If you’re using a Mac or Linux:rm -f *# Or, if you’re on Windows:del *# Then, switch back to the project foldercd ..
現在在專案資料夾中執行 npm start
,並在瀏覽器中開啟 https://#:3000
。您應該會看到一個電子郵件輸入欄位和一個提交按鈕。
我們建議您按照這些說明為您的編輯器設定語法突顯。
如果您卡住了,請查看 Formik 的 GitHub 討論區。此外,Formium 社群 Discord 伺服器也是一個快速獲得幫助的好方法。如果您沒有收到答案,或者您仍然卡住,請提交 issue,我們會幫助您。
Formik 是一小组 React 組件和 hooks,用於在 React 和 React Native 中構建表單。它有助於處理三個最惱人的部分
Formik 將以上所有功能整合在一起,讓表單的測試、重構和邏輯推理變得輕而易舉,井然有序。
我們將從使用 Formik 最 *繁瑣* 的方式開始。雖然這看起來有點冗長,但了解 Formik 的建構方式非常重要,這樣您才能 volledig 掌握其功能,並建立完整的心智模型來理解其運作原理。
假設我們想為部落格新增一個電子報註冊表單。首先,我們的表單只有一個名為 email
的欄位。使用 Formik,只需幾行程式碼即可完成。
import React from 'react';import { useFormik } from 'formik';const SignupForm = () => {// Pass the useFormik() hook initial form values and a submit function that will// be called when the form is submittedconst formik = useFormik({initialValues: {email: '',},onSubmit: values => {alert(JSON.stringify(values, null, 2));},});return (<form onSubmit={formik.handleSubmit}><label htmlFor="email">Email Address</label><inputid="email"name="email"type="email"onChange={formik.handleChange}value={formik.values.email}/><button type="submit">Submit</button></form>);};
我們將表單的 initialValues
(初始值)和一個提交函式 (onSubmit
) 傳遞給 useFormik()
hook。然後,這個 hook 會將表單狀態和輔助方法的集合回傳給我們,我們將其儲存在名為 formik
的變數中。目前,我們只關心以下輔助方法:
handleSubmit
:提交處理函式handleChange
:要傳遞給每個 <input>
、<select>
或 <textarea>
的變更處理函式values
:我們表單的目前值如上所示,我們將這些方法分別傳遞給它們各自的 props...就這樣!我們現在有了一個由 Formik 支援的功能表單。我們不再需要自行管理表單的值,也不需要為每個輸入框撰寫自定義事件處理函式,只需使用 useFormik()
即可。
這很簡潔,但只有一個輸入框時,使用 useFormik()
的好處不明顯。所以讓我們再新增兩個輸入框:一個用於使用者的名字和姓氏,我們將它們分別儲存為表單中的 firstName
和 lastName
。
import React from 'react';import { useFormik } from 'formik';const SignupForm = () => {// Note that we have to initialize ALL of fields with values. These// could come from props, but since we don’t want to prefill this form,// we just use an empty string. If we don’t do this, React will yell// at us.const formik = useFormik({initialValues: {firstName: '',lastName: '',email: '',},onSubmit: values => {alert(JSON.stringify(values, null, 2));},});return (<form onSubmit={formik.handleSubmit}><label htmlFor="firstName">First Name</label><inputid="firstName"name="firstName"type="text"onChange={formik.handleChange}value={formik.values.firstName}/><label htmlFor="lastName">Last Name</label><inputid="lastName"name="lastName"type="text"onChange={formik.handleChange}value={formik.values.lastName}/><label htmlFor="email">Email Address</label><inputid="email"name="email"type="email"onChange={formik.handleChange}value={formik.values.email}/><button type="submit">Submit</button></form>);};
如果您仔細觀察我們的新程式碼,您會注意到一些模式和對稱性正在 *形成*。
handleChange
initialValues
中定義的屬性 *相符* 的 id
和 name
HTML 屬性email
-> formik.values.email
)如果您熟悉使用原生 React 建立表單,您可以將 Formik 的 handleChange
理解為以下這樣的工作方式:
const [values, setValues] = React.useState({});const handleChange = event => {setValues(prevValues => ({...prevValues,// we use the name to tell Formik which key of `values` to update[event.target.name]: event.target.value});}
雖然我們的聯絡表單可以運作,但功能還不夠完整;使用者可以提交表單,但表單不會告知他們哪些欄位是必填的(如果有的話)。
如果我們可以使用瀏覽器內建的 HTML 輸入驗證,我們可以為每個輸入框新增一個 required
屬性,指定最小/最大長度(maxlength
和 minlength
),和/或為每個輸入框新增一個 pattern
屬性以進行正規表達式驗證。如果可以的話,這些方法都很棒。然而,HTML 驗證有其局限性。首先,它只能在瀏覽器中運作!所以這顯然不適用於 React Native。其次,很難/不可能向使用者顯示自定義錯誤訊息。第三,它很不穩定。
如前所述,Formik 不僅會追蹤表單的 values
(值),還會追蹤其驗證和錯誤訊息。要使用 JS 新增驗證,讓我們指定一個自定義驗證函式,並將其作為 validate
傳遞給 useFormik()
hook。如果存在錯誤,這個自定義驗證函式應產生一個與我們的 values
/initialValues
形狀相符的 error
物件。再次...*對稱性*...是的...
import React from 'react';import { useFormik } from 'formik';// A custom validation function. This must return an object// which keys are symmetrical to our values/initialValuesconst validate = values => {const errors = {};if (!values.firstName) {errors.firstName = 'Required';} else if (values.firstName.length > 15) {errors.firstName = 'Must be 15 characters or less';}if (!values.lastName) {errors.lastName = 'Required';} else if (values.lastName.length > 20) {errors.lastName = 'Must be 20 characters or less';}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;};const SignupForm = () => {// Pass the useFormik() hook initial form values, a validate function that will be called when// form values change or fields are blurred, and a submit function that will// be called when the form is submittedconst formik = useFormik({initialValues: {firstName: '',lastName: '',email: '',},validate,onSubmit: values => {alert(JSON.stringify(values, null, 2));},});return (<form onSubmit={formik.handleSubmit}><label htmlFor="firstName">First Name</label><inputid="firstName"name="firstName"type="text"onChange={formik.handleChange}value={formik.values.firstName}/>{formik.errors.firstName ? <div>{formik.errors.firstName}</div> : null}<label htmlFor="lastName">Last Name</label><inputid="lastName"name="lastName"type="text"onChange={formik.handleChange}value={formik.values.lastName}/>{formik.errors.lastName ? <div>{formik.errors.lastName}</div> : null}<label htmlFor="email">Email Address</label><inputid="email"name="email"type="email"onChange={formik.handleChange}value={formik.values.email}/>{formik.errors.email ? <div>{formik.errors.email}</div> : null}<button type="submit">Submit</button></form>);};
formik.errors
是透過自定義驗證函式填入的。預設情況下,Formik 會在每次按鍵(變更事件)、每個輸入框的 blur 事件 以及提交之前進行驗證。我們傳遞給 useFormik()
的 onSubmit
函式只有在沒有錯誤的情況下才會執行(即,如果我們的 validate
函式回傳 {}
)。
雖然我們的表單可以運作,而且我們的使用者可以看到每個錯誤,但这對他們來說並不是一個很好的使用者體驗。由於我們的驗證函式會在每次按鍵時針對 *整個* 表單的 values
執行,因此我們的 errors
物件在任何時刻都包含 *所有* 驗證錯誤。在我們的元件中,我們只是檢查是否存在錯誤,然後立即將其顯示給使用者。這很尷尬,因為我們會顯示使用者甚至尚未訪問過的欄位的錯誤訊息。大多數情況下,我們只想在使用者 *完成* 在該欄位中輸入內容 *後* 才顯示該欄位的錯誤訊息。
與 errors
和 values
類似,Formik 會追蹤哪些欄位已被訪問。它將此資訊儲存在一個名為 touched
的物件中,該物件也反映了 values
/initialValues
的形狀。touched
的鍵是欄位名稱,而 touched
的值是布林值 true
/false
。
為了利用 touched
,我們將 formik.handleBlur
傳遞給每個輸入框的 onBlur
屬性。這個函式的運作方式與 formik.handleChange
類似,它使用 name
屬性來判斷要更新哪個欄位。
import React from 'react';import { useFormik } from 'formik';const validate = values => {const errors = {};if (!values.firstName) {errors.firstName = 'Required';} else if (values.firstName.length > 15) {errors.firstName = 'Must be 15 characters or less';}if (!values.lastName) {errors.lastName = 'Required';} else if (values.lastName.length > 20) {errors.lastName = 'Must be 20 characters or less';}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;};const SignupForm = () => {const formik = useFormik({initialValues: {firstName: '',lastName: '',email: '',},validate,onSubmit: values => {alert(JSON.stringify(values, null, 2));},});return (<form onSubmit={formik.handleSubmit}><label htmlFor="firstName">First Name</label><inputid="firstName"name="firstName"type="text"onChange={formik.handleChange}onBlur={formik.handleBlur}value={formik.values.firstName}/>{formik.errors.firstName ? <div>{formik.errors.firstName}</div> : null}<label htmlFor="lastName">Last Name</label><inputid="lastName"name="lastName"type="text"onChange={formik.handleChange}onBlur={formik.handleBlur}value={formik.values.lastName}/>{formik.errors.lastName ? <div>{formik.errors.lastName}</div> : null}<label htmlFor="email">Email Address</label><inputid="email"name="email"type="email"onChange={formik.handleChange}onBlur={formik.handleBlur}value={formik.values.email}/>{formik.errors.email ? <div>{formik.errors.email}</div> : null}<button type="submit">Submit</button></form>);};
快完成了!現在我們正在追蹤 touched
,我們可以將錯誤訊息渲染邏輯更改為 *僅* 在錯誤存在 *且* 使用者已訪問該欄位時才顯示該欄位的錯誤訊息。
import React from 'react';import { useFormik } from 'formik';const validate = values => {const errors = {};if (!values.firstName) {errors.firstName = 'Required';} else if (values.firstName.length > 15) {errors.firstName = 'Must be 15 characters or less';}if (!values.lastName) {errors.lastName = 'Required';} else if (values.lastName.length > 20) {errors.lastName = 'Must be 20 characters or less';}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;};const SignupForm = () => {const formik = useFormik({initialValues: {firstName: '',lastName: '',email: '',},validate,onSubmit: values => {alert(JSON.stringify(values, null, 2));},});return (<form onSubmit={formik.handleSubmit}><label htmlFor="firstName">First Name</label><inputid="firstName"name="firstName"type="text"onChange={formik.handleChange}onBlur={formik.handleBlur}value={formik.values.firstName}/>{formik.touched.firstName && formik.errors.firstName ? (<div>{formik.errors.firstName}</div>) : null}<label htmlFor="lastName">Last Name</label><inputid="lastName"name="lastName"type="text"onChange={formik.handleChange}onBlur={formik.handleBlur}value={formik.values.lastName}/>{formik.touched.lastName && formik.errors.lastName ? (<div>{formik.errors.lastName}</div>) : null}<label htmlFor="email">Email Address</label><inputid="email"name="email"type="email"onChange={formik.handleChange}onBlur={formik.handleBlur}value={formik.values.email}/>{formik.touched.email && formik.errors.email ? (<div>{formik.errors.email}</div>) : null}<button type="submit">Submit</button></form>);};
如上所示,驗證由您決定。您可以隨意撰寫自己的驗證器或使用第三方輔助程式庫。Formik 的作者/其大部分使用者使用 Jason Quense 的程式庫 Yup 進行物件 schema 驗證。Yup 有一個類似於 Joi 和 React PropTypes 的 API,但也足夠小,可以在瀏覽器中使用,並且速度足夠快,可以在執行時使用。您可以使用此 REPL 在這裡試用它。
由於 Formik 作者/使用者非常 *喜歡* Yup,因此 Formik 有一個針對 Yup 的特殊設定屬性,稱為 validationSchema
,它會自動將 Yup 的驗證錯誤訊息轉換為一個漂亮的物件,其鍵與 values
/initialValues
/touched
相符(就像任何自定義驗證函式一樣)。無論如何,您可以從 NPM/yarn 安裝 Yup,如下所示...
npm install yup --save# or via yarnyarn add yup
要了解 Yup 的運作方式,讓我們擺脫自定義驗證函式 validate
,並使用 Yup 和 validationSchema
重新撰寫我們的驗證
import React from 'react';import { useFormik } from 'formik';import * as Yup from 'yup';const SignupForm = () => {const formik = useFormik({initialValues: {firstName: '',lastName: '',email: '',},validationSchema: Yup.object({firstName: Yup.string().max(15, 'Must be 15 characters or less').required('Required'),lastName: Yup.string().max(20, 'Must be 20 characters or less').required('Required'),email: Yup.string().email('Invalid email address').required('Required'),}),onSubmit: values => {alert(JSON.stringify(values, null, 2));},});return (<form onSubmit={formik.handleSubmit}><label htmlFor="firstName">First Name</label><inputid="firstName"name="firstName"type="text"onChange={formik.handleChange}onBlur={formik.handleBlur}value={formik.values.firstName}/>{formik.touched.firstName && formik.errors.firstName ? (<div>{formik.errors.firstName}</div>) : null}<label htmlFor="lastName">Last Name</label><inputid="lastName"name="lastName"type="text"onChange={formik.handleChange}onBlur={formik.handleBlur}value={formik.values.lastName}/>{formik.touched.lastName && formik.errors.lastName ? (<div>{formik.errors.lastName}</div>) : null}<label htmlFor="email">Email Address</label><inputid="email"name="email"type="email"onChange={formik.handleChange}onBlur={formik.handleBlur}value={formik.values.email}/>{formik.touched.email && formik.errors.email ? (<div>{formik.errors.email}</div>) : null}<button type="submit">Submit</button></form>);};
同樣,Yup 是 100% 可選的。但是,我們建議您嘗試一下。如上所示,我們只用 10 行程式碼就表達了完全相同的驗證函式,而不是 30 行。Formik 的核心設計原則之一是幫助您保持條理性。Yup 無疑在這方面提供了很大的幫助——schema 表達能力強,直觀(因為它們反映了您的值),並且可重複使用。無論您是否使用 Yup,我們都強烈建議您在應用程式中分享常用的驗證方法。這將確保通用欄位(例如電子郵件、街道地址、使用者名稱、電話號碼等)的驗證方式一致,並帶來更好的使用者體驗。
getFieldProps()
以上程式碼非常明確地展示了 Formik 的運作方式。onChange
-> handleChange
,onBlur
-> handleBlur
,依此類推。然而,為了節省您的時間,useFormik()
返回一個名為 formik.getFieldProps()
的輔助方法,以便更快地連接輸入欄位。給定一些欄位級別的資訊,它會返回給您一組針對特定欄位的 onChange
、onBlur
、value
、checked
。然後,您可以將其展開應用於 input
、select
或 textarea
。
import React from 'react';import { useFormik } from 'formik';import * as Yup from 'yup';const SignupForm = () => {const formik = useFormik({initialValues: {firstName: '',lastName: '',email: '',},validationSchema: Yup.object({firstName: Yup.string().max(15, 'Must be 15 characters or less').required('Required'),lastName: Yup.string().max(20, 'Must be 20 characters or less').required('Required'),email: Yup.string().email('Invalid email address').required('Required'),}),onSubmit: values => {alert(JSON.stringify(values, null, 2));},});return (<form onSubmit={formik.handleSubmit}><label htmlFor="firstName">First Name</label><inputid="firstName"type="text"{...formik.getFieldProps('firstName')}/>{formik.touched.firstName && formik.errors.firstName ? (<div>{formik.errors.firstName}</div>) : null}<label htmlFor="lastName">Last Name</label><input id="lastName" type="text" {...formik.getFieldProps('lastName')} />{formik.touched.lastName && formik.errors.lastName ? (<div>{formik.errors.lastName}</div>) : null}<label htmlFor="email">Email Address</label><input id="email" type="email" {...formik.getFieldProps('email')} />{formik.touched.email && formik.errors.email ? (<div>{formik.errors.email}</div>) : null}<button type="submit">Submit</button></form>);};
我們的程式碼再次非常明確地說明了 Formik 的運作方式。onChange
-> handleChange
,onBlur
-> handleBlur
,依此類推。然而,我們仍然必須手動將這個「屬性獲取器」getFieldProps()
傳遞給每個輸入欄位。為了節省您更多的時間,Formik 附帶了基於 React Context 的 API/元件,讓您的生活更輕鬆,程式碼更簡潔:<Formik />
、<Form />
、<Field />
和 <ErrorMessage />
。更明確地說,它們隱式地使用 React Context 來連接到父級 <Formik />
的狀態/方法。
由於這些元件使用 React Context,我們需要在我們的樹狀結構中渲染一個包含表單狀態和輔助方法的 React Context Provider。如果您自己這樣做,它看起來會像這樣
import React from 'react';import { useFormik } from 'formik';// Create empty contextconst FormikContext = React.createContext({});// Place all of what’s returned by useFormik into contextexport const Formik = ({ children, ...props }) => {const formikStateAndHelpers = useFormik(props);return (<FormikContext.Provider value={formikStateAndHelpers}>{typeof children === 'function'? children(formikStateAndHelpers): children}</FormikContext.Provider>);};
幸運的是,我們已經在 <Formik>
元件中為您完成了這項工作,它的工作原理與此相同。
現在讓我們將 useFormik()
hook 換成 Formik 的 <Formik>
元件/渲染屬性。由於它是一個元件,我們會將傳遞給 useFormik()
的物件轉換為 JSX,每個鍵都將成為一個屬性。
import React from 'react';import { Formik } from 'formik';import * as Yup from 'yup';const SignupForm = () => {return (<FormikinitialValues={{ firstName: '', lastName: '', email: '' }}validationSchema={Yup.object({firstName: Yup.string().max(15, 'Must be 15 characters or less').required('Required'),lastName: Yup.string().max(20, 'Must be 20 characters or less').required('Required'),email: Yup.string().email('Invalid email address').required('Required'),})}onSubmit={(values, { setSubmitting }) => {setTimeout(() => {alert(JSON.stringify(values, null, 2));setSubmitting(false);}, 400);}}>{formik => (<form onSubmit={formik.handleSubmit}><label htmlFor="firstName">First Name</label><inputid="firstName"type="text"{...formik.getFieldProps('firstName')}/>{formik.touched.firstName && formik.errors.firstName ? (<div>{formik.errors.firstName}</div>) : null}<label htmlFor="lastName">Last Name</label><inputid="lastName"type="text"{...formik.getFieldProps('lastName')}/>{formik.touched.lastName && formik.errors.lastName ? (<div>{formik.errors.lastName}</div>) : null}<label htmlFor="email">Email Address</label><input id="email" type="email" {...formik.getFieldProps('email')} />{formik.touched.email && formik.errors.email ? (<div>{formik.errors.email}</div>) : null}<button type="submit">Submit</button></form>)}</Formik>);};
如您在上面看到的,我們將 useFormik()
hook 換成了 <Formik>
元件。<Formik>
元件接受一個函式作為其子項(也就是 渲染屬性)。它的參數與 useFormik()
返回的物件完全相同(實際上,<Formik>
在內部調用了 useFormik()
!)。因此,我們的表單與以前一樣工作,只是現在我們可以使用新的元件以更簡潔的方式表達自己。
import React from 'react';import { Formik, Field, Form, ErrorMessage } from 'formik';import * as Yup from 'yup';const SignupForm = () => {return (<FormikinitialValues={{ firstName: '', lastName: '', email: '' }}validationSchema={Yup.object({firstName: Yup.string().max(15, 'Must be 15 characters or less').required('Required'),lastName: Yup.string().max(20, 'Must be 20 characters or less').required('Required'),email: Yup.string().email('Invalid email address').required('Required'),})}onSubmit={(values, { setSubmitting }) => {setTimeout(() => {alert(JSON.stringify(values, null, 2));setSubmitting(false);}, 400);}}><Form><label htmlFor="firstName">First Name</label><Field name="firstName" type="text" /><ErrorMessage name="firstName" /><label htmlFor="lastName">Last Name</label><Field name="lastName" type="text" /><ErrorMessage name="lastName" /><label htmlFor="email">Email Address</label><Field name="email" type="email" /><ErrorMessage name="email" /><button type="submit">Submit</button></Form></Formik>);};
預設情況下,<Field>
元件會渲染一個 <input>
元件,給定一個 name
屬性,它會隱式地抓取相應的 onChange
、onBlur
、value
屬性並將它們傳遞給元素,以及您傳遞給它的任何屬性。然而,由於並非所有東西都是輸入欄位,<Field>
也接受其他一些屬性,讓您可以渲染任何您想要的東西。以下是一些例子。
// <input className="form-input" placeHolder="Jane" /><Field name="firstName" className="form-input" placeholder="Jane" />// <textarea className="form-textarea"/></textarea><Field name="message" as="textarea" className="form-textarea" />// <select className="my-select"/><Field name="colors" as="select" className="my-select"><option value="red">Red</option><option value="green">Green</option><option value="blue">Blue</option></Field>
React 的核心是組合,雖然我們已經減少了很多 屬性鑽取,但我們仍然在每個輸入欄位中重複使用 label
、<Field>
和 <ErrorMessage>
。我們可以用抽象化做得更好!使用 Formik,您可以而且應該構建可重複使用的輸入欄位基本元件,並在您的應用程式中共享它們。事實證明,我們的 <Field>
渲染屬性元件有一個姐妹,她名叫 useField
,她將會做同樣的事情,但透過 React Hooks!看看這個...
import React from 'react';import ReactDOM from 'react-dom';import { Formik, Form, useField } from 'formik';import * as Yup from 'yup';const MyTextInput = ({ label, ...props }) => {// useField() returns [formik.getFieldProps(), formik.getFieldMeta()]// which we can spread on <input>. We can use field meta to show an error// message if the field is invalid and it has been touched (i.e. visited)const [field, meta] = useField(props);return (<><label htmlFor={props.id || props.name}>{label}</label><input className="text-input" {...field} {...props} />{meta.touched && meta.error ? (<div className="error">{meta.error}</div>) : null}</>);};const MyCheckbox = ({ children, ...props }) => {// React treats radios and checkbox inputs differently from other input types: select and textarea.// Formik does this too! When you specify `type` to useField(), it will// return the correct bag of props for you -- a `checked` prop will be included// in `field` alongside `name`, `value`, `onChange`, and `onBlur`const [field, meta] = useField({ ...props, type: 'checkbox' });return (<div><label className="checkbox-input"><input type="checkbox" {...field} {...props} />{children}</label>{meta.touched && meta.error ? (<div className="error">{meta.error}</div>) : null}</div>);};const MySelect = ({ label, ...props }) => {const [field, meta] = useField(props);return (<div><label htmlFor={props.id || props.name}>{label}</label><select {...field} {...props} />{meta.touched && meta.error ? (<div className="error">{meta.error}</div>) : null}</div>);};// And now we can use theseconst SignupForm = () => {return (<><h1>Subscribe!</h1><FormikinitialValues={{firstName: '',lastName: '',email: '',acceptedTerms: false, // added for our checkboxjobType: '', // added for our select}}validationSchema={Yup.object({firstName: Yup.string().max(15, 'Must be 15 characters or less').required('Required'),lastName: Yup.string().max(20, 'Must be 20 characters or less').required('Required'),email: Yup.string().email('Invalid email address').required('Required'),acceptedTerms: Yup.boolean().required('Required').oneOf([true], 'You must accept the terms and conditions.'),jobType: Yup.string().oneOf(['designer', 'development', 'product', 'other'],'Invalid Job Type').required('Required'),})}onSubmit={(values, { setSubmitting }) => {setTimeout(() => {alert(JSON.stringify(values, null, 2));setSubmitting(false);}, 400);}}><Form><MyTextInputlabel="First Name"name="firstName"type="text"placeholder="Jane"/><MyTextInputlabel="Last Name"name="lastName"type="text"placeholder="Doe"/><MyTextInputlabel="Email Address"name="email"type="email"placeholder="jane@formik.com"/><MySelect label="Job Type" name="jobType"><option value="">Select a job type</option><option value="designer">Designer</option><option value="development">Developer</option><option value="product">Product Manager</option><option value="other">Other</option></MySelect><MyCheckbox name="acceptedTerms">I accept the terms and conditions</MyCheckbox><button type="submit">Submit</button></Form></Formik></>);};
如您在上面看到的,useField()
使我們能夠將任何類型的 React 元件輸入連接到 Formik,就像它是 <Field>
+ <ErrorMessage>
一樣。我們可以用它來構建一組滿足我們需求的可重複使用的輸入欄位。
恭喜!您已經使用 Formik 建立了一個註冊表單,它
做得好!我們希望您現在對 Formik 的工作原理有了一定的了解。
在此處查看最終結果:最終結果。
如果您有額外的時間或想練習您新的 Formik 技能,以下是一些您可以對註冊表單進行改進的想法,這些想法按難度遞增順序列出
formik.isSubmitting
)formik.handleReset
或 <button type="reset">
新增重置按鈕。<SignupForm>
的屬性預先填入 initialValues
。在本教學課程中,我們介紹了 Formik 的概念,包括表單狀態、欄位、驗證、hook、渲染屬性和 React context。有關每個主題的更詳細說明,請查看其餘的 文件。要了解有關定義教學課程中的元件和 hook 的更多資訊,請查看 API 參考。