Formik是一個專為React構建的開源表單庫。它提供了一個易于使用的API來處理表單狀態管理,表單驗證以及表單提交。Formik支持React中的所有表單元素和事件,可以很好地與React生態系統中的其他庫集成。同時,Formik還提供了一些高級功能,如支持異步驗證、字段級別的驗證、根據表單狀態變化自動計算屬性等。
Formik 基礎
Formik的優勢,相較于傳統的表單處理方法,Formik具有以下優勢:
- 狀態管理: Formik自動地處理表單狀態管理,無需手動編寫大量的狀態管理邏輯。
- 表單驗證: Formik提供了易于使用的表單驗證工具,可以快速實現表單驗證邏輯。
- 表單提交: Formik可以方便地處理表單提交,包括異步表單提交和重試機制。
- 支持復雜表單: Formik支持處理包括多級嵌套、動態表單、數組表單等多種復雜表單。
Formik 安裝和簡單使用
1、Formik安裝,使用npm或yarn安裝Formik。在終端中,切換項目目錄并運行此命令 npm install formik
或 yarn add formik
。
2、表單數據綁定以及提交處理
useFormik HOOK 參數說明:
- initialValues:傳遞定義的輸入HTML元素的name屬性相匹配的初始值
- onSubmit:提交程序,已經默認阻止了默認行為
onSubmit 屬性定義了一個回調函數,在表單提交時被調用。在回調函數中可以訪問表單中所有輸入框的值并執行提交操作,通過 setSubmitting 函數,可以設置表單的提交狀態。在表單的渲染函數中通過 handleSubmit 屬性來處理表單的提交,使用 isSubmitting(默認false) 屬性來禁用提交按鈕,直到表單提交完成。
const formik = useFormik({initialValues: {username: '',password: ''},onSubmit: (values, {setSubmitting}) => {// formik 已經默認幫我們阻止了默認執行console.log('values: ', values);setSubmitting(true)}})……<form onSubmit={formik.handleSubmit}><div><label htmlFor="username">用戶名:</label><input id="username" type="text" name="username" value={formik.values.username} onChange={formik.handleChange}></input></div><div><label htmlFor="password">密碼:</label><input id="password" type="password" name="password" value={formik.values.password} onChange={formik.handleChange}></input></div><button type="submit" disabled={formik.isSubmitting}>提交</button></form>
useFormik 返回對象屬性解釋:
- handleSubmit:提交處理程序
- handleChange:傳遞給每個
<input>
、<select>
、 或<textarea>
的更改處理程序 - values:表單的當前值,使用 name 訪問對應的值
- handleChange:每個 HTML 輸入重用相同的更改處理函數
- setValues和setFieldValue:setValues設置formik的values屬性值,setFieldValue設置values的某一個屬性值,二者寫法不同,但是目的一樣改變values的值。
如果沒有 formik,那么就得一個個寫change事件。
3、表單校驗
Formik 不僅跟蹤表單values,還跟蹤其驗證和錯誤消息。要使用 JS 添加驗證,需指定一個自定義驗證函數并將其傳遞給鉤子 useFormik()
的參數 validate。如果存在錯誤,這個自定義驗證函數應該生成一個匹配的error對象。
validate: values => {const errors = {}if (!values.username) {errors.username = '請輸入用戶名'} else if (values.username.length < 6 || values.username.length > 16) {errors.username = '請輸入6~16位的用戶名'} else if (!/^\w{6,16}$/.test(values.username)) {errors.username = '請輸入由字母、數字、下劃線組成的用戶名'}if (!values.password) {errors.password = '請輸入密碼'} else if (values.password.length < 6 || values.password.length > 16) {errors.password = '請輸入6~16位的密碼'} else if (!/^\w{6,16}$/.test(values.password)) {errors.password = '請輸入由字母、數字、下劃線組成的密碼'}console.log('errors: ', errors);return errors}
默認情況下,Formik 將在每次擊鍵(onChange)事件以及提交之前進行驗證。傳遞 formik.handleBlur
給輸入元素的 onBlur
屬性,那么會在輸入元素的失焦(onBlur)事件中進行驗證:
<input id="username" type="text" name="username" value={formik.values.username} onChange={formik.handleChange} onBlur={formik.handleBlur}></input>
校驗確實實現了,但是如果希望在提交表單的時候再顯示錯誤,需要怎么做呢?
Formik 會跟蹤哪些字段已被訪問過。它將這些信息存儲在一個名為 touched 的對象中,這些值為boolean值。
<p>{formik.touched.username && formik.errors.username ? formik.errors.username : ''}</p>
Formik 結合 Yup 進行表單驗證
Yup是一個構建對象模式的JavaScript模式驗證器,用于驗證和解析數據。它提供了一種聲明式方法來創建校驗模式。
由于 Formik 作者/用戶非常喜歡Yup,因此 Formik 有一個名為 Yup 的特殊配置道具validationSchema,它會自動將 Yup 的驗證錯誤消息轉換為一對象。
1、Yup 安裝 npm install yup
或 yarn add yup
2、使用 Yup 校驗表單:
- Formik 結合 Yup 可以輕松實現表單驗證
validationSchema={Yup.object({...})}
,使用 validationSchema 和 Yup 搭配之后就不需要再使用validate配置了。 - Yup 允許創建復雜的校驗邏輯,如必填項、字符長度、正則表達式等。
validationSchema: Yup.object({username: Yup.string().required('請輸入用戶名').min(6, '請輸入6~16位的用戶名').max(16, '請輸入6~16位的用戶名').matches(/^\w{6,16}$/, '請輸入由字母、數字、下劃線組成的用戶名'), password: Yup.string().required('請輸入密碼').min(6, '請輸入6~16位的密碼').max(16, '請輸入6~16位的密碼').matches(/^\w{6,16}$/, '請輸入由字母、數字、下劃線組成的密碼')})
Formik 組件
以上都是通過 useFormik 拿到控制formik表單的內容,但是這樣不容易形成封裝,也就是說無法進行實例傳值。而Formik由于配備了React Context,因此Formik本身就可以管理所包裹的JSX。
formik完全遵守React的組件化原則,可以和其他庫或自定義邏輯無縫集成:
- formik支持構建和復用表單組件,通過
<Formik />
組件和Context API實現跨組件通信。 - 可以創建包裝
<Field />
、ErrorMessage
等的自定義組件,提高代碼復用性和可維護性。
<Formik initialValues={{username: '', password: ''}} // 設置初始化值onSubmit={(values, {setSubmitting}) => { // 提交表單執行的函數setTimeout(() => {console.log('values: ', values); setSubmitting(true)}, 2000);}}validationSchema={Yup.object({ // 設置表單校驗的模式username: Yup.string().required('請輸入用戶名').min(6, '請輸入6~16位的用戶名').max(16, '請輸入6~16位的用戶名').matches(/^\w{6,16}$/, '請輸入由字母、數字、下劃線組成的用戶名'), password: Yup.string().required('請輸入密碼').min(6, '請輸入6~16位的密碼').max(16, '請輸入6~16位的密碼').matches(/^\w{6,16}$/, '請輸入由字母、數字、下劃線組成的密碼')})}>{({handleSubmit, values, touched, handleChange, handleBlur, errors, isSubmitting}) => {return <form onSubmit={handleSubmit}><div><label htmlFor="username1">用戶名:</label>{/* <input id="username1" type="text" name="username" value={values.username} onChange={handleChange} onBlur={handleBlur}></input> */}<Field id='username1' name='username' type='text'></Field>{/* <p>{touched.username && errors.username ? errors.username : ''}</p> */}<ErrorMessage name="username"></ErrorMessage></div><div><label htmlFor="password1">密碼:</label><input id="password1" type="password" name="password" value={values.password} onChange={handleChange}></input><p>{touched.password && errors.password ? errors.password : ''}</p></div><button type="submit" disabled={isSubmitting}>提交</button></form>}}</Formik>
常用組件
1、ErrorMessage 捕捉錯誤的容器,必傳一個name屬性。
<ErrorMessage name="username"></ErrorMessage>
2、Field 將自動將輸入連接到 Formik,默認渲染是input輸入框,有以下幾種方法渲染:
function MyField({field, form}) {console.log('field: ', field);console.log('form: ', form);return <input {...field}></input>}……<Formik initialValues={{email: '', project: 2, phone: '', address: '1'}} // 設置初始化值onSubmit={(values) => { console.log('values: ', values); }} // 提交表單執行的函數validationSchema={Yup.object({ // 設置表單校驗的模式phone: Yup.string().required('請輸入電話號碼').matches(/^1[0-9]{10}/, '請輸入有效的電話號碼')})}>{({handleSubmit}) => {return <form onSubmit={handleSubmit}>{/* 1、直接渲染 */}<Field type='email' name='email' placeholder='請輸入電子郵箱'></Field> <br/>{/* 2、as 轉化成其他節點 */}<Field as='select' name='project'><option value={1}>數學及應用數學</option><option value={2}>軟件工程</option><option value={3}>國際貿易</option></Field> <br/>{/* 3、渲染 jsx */}<Field name='phone'>{({field, form, meta}) => {console.log('field: ', field);console.log('form: ', form);console.log('meta: ', meta);return <div><label htmlFor="phone">電話號碼</label><input id="phone" type="text" placeholder="請輸入電話號碼" value={field.value.phone} {...field}></input><p>{ meta.touched && meta.error ? meta.error : ''}</p></div>}}</Field>{/* 4、以 component 組件形式渲染 */}<Field name='address' component={MyField}></Field> <br /><button type="submit">提交</button></form>}}</Formik>
數組和對象校驗
Formik 支持嵌套對象和數組:
- yup 對象校驗使用
Yup.object({})
- yup 數組校驗使用
Yup.array().of()
<FormikinitialValues={{social: {wechat: '', qq: ''}, frends: ['', '']}}onSubmit={(values, {setSubmitting}) => {console.log('values: ', values)setSubmitting(true)}} validationSchema={Yup.object({social: Yup.object({wechat: Yup.string().required('請輸入微信'), qq: Yup.string().required('請輸入QQ').matches(/^[\w]$/, '請輸入有效的QQ')}),frends: Yup.array().of(Yup.string().required('請輸入你的朋友'))})}>{({values, handleChange, handleBlur, touched, errors, isSubmitting, setValues, setFieldValue}) => {console.log('object array values: ', values);console.log('object array handleChange: ', handleChange);console.log('object array handleBlur: ', handleBlur);console.log('object array touched: ', touched);console.log('object array errors: ', errors);console.log('object array setValues: ', setValues);console.log('object array setFieldValue: ', setFieldValue);return <form><h4>社交賬號</h4><label>微信:</label><input name="social.wechat" value={values.social.wechat} onChange={handleChange} onBlur={handleBlur}></input><p>{ touched.social && errors.social && touched.social.wechat && errors.social.wechat ? errors.social.wechat : ''}</p><br /><label>QQ:</label><input name="social.qq" value={values.social.qq} onChange={handleChange} onBlur={handleBlur}></input><h4>朋友</h4>{values.frends.map((v, i) => {return (<div key={i}><input name={`friends[${i}]`} onChange={handleChange} onBlur={handleBlur}></input>{i < values.frends.length && <br />}</div>)})}<button type="submit" disabled={isSubmitting}>提交</button></form>}}</Formik>