[note] Formik 筆記
- Formik @ Official Website
- Formik Examples @ Github
- Formik Debug Component @ Github
關於那些小細節 Tips
- Formik 會使用
<input>
欄位中的name
屬性來決定該value
要保存在哪個內部的 key 中。 - Formik 中包括
values
,errors
,touched
物件,裡面的key
都會和<input>
的name
對應。 - Formik 在使用 radio button 時,當帶入的 value 是「數值」需特別留意,因為存在
field
的值會是「數值」,但存入 Formik 的meta
中會變成「字串」,這會導致 Formik 沒辦法匹配正確,進而使得該 input 沒辦法被勾選(checked),但在 Formik 中又會設值的情況(參考這個 Gist)。
useFormik
基本使用
使用 Formik 中的 values
物件:
import { useFormik } from 'formik';
const SignupForm = () => {
// STEP 1: useFormik
const formik = useFormik({
// STEP 2-1:建立初始值
initialValues: { email: '' },
// STEP 2-2:在 onSubmit 時 透過 `values` 物件取得表單中的內容
onSubmit: values => {
console.log(JSON.stringify(values, null, 2));
},
});
return (
// STEP 3: 在 onSubmit 事件中帶入 `formik.handleSubmit`
<form onSubmit={formik.handleSubmit}>
<div>
<label htmlFor="email">Email Address</label>
{/* STEP 4-1:建立 input 欄位,formik 會用 input 的 name 來作為內部的 key */}
<input id="email" name="email" type="email"
{/* STEP 4-2: invoke `formik.handleChange` when input onchange */}
onChange={formik.handleChange}
{/* STEP 4-3: 使用 values 物件取回該欄位的值 */}
value={formik.values.email}
/>
</div>
<button type="submit">Submit</button>
</form>
);
};
表單驗證:手動處理(validate)
使用 Formik 中的 errors
物件,並搭配 validate
屬性:
// STEP 1:建立 validate
const validate = (values) => {
const errors = {};
if (!values.firstName) {
errors.firstName = 'Required first name';
} else if (values.firstName.length > 15) {
errors.firstName = 'Must be 15 characters or less';
}
return errors;
};
const SignupForm = () => {
const formik = useFormik({
initialValues: { email: '' },
// STEP 2:放入 validate 函式
validate,
onSubmit: (values) => {
console.log(JSON.stringify(values, null, 2));
},
});
return (
<form onSubmit={formik.handleSubmit}>
<div>
<label htmlFor="email">Email Address</label>
<input
id="email"
name="email"
type="email"
onChange={formik.handleChange}
value={formik.values.email}
/>
{/* STEP 3: 有錯誤時顯示錯誤 */}
{formik.errors.email ? <div>{formik.errors.email}</div> : null}
</div>
<button type="submit">Submit</button>
</form>
);
};
- 預設的情況下,在每一次 onChange 及 onSubmit 時,Formik 都會促發 validate 函式(如果有給的話),只有在
errors
為空物件時,才會真的把表單送出。
表單驗證:搭配 Yup(validationSchema)
在 Formik 中除了 validate
可以手動驗證表的欄外之外,額外提供 validationSchema
可以搭配 Yup 來做欄位的資料驗證:
import React from 'react';
import { useFormik } from 'formik';
// STEP 1:匯入 Yup
import * as Yup from 'yup';
const SignupForm = () => {
const formik = useFormik({
initialValues: { email: '' },
// STEP 2:搭配 Yup 使用 validationSchema
validationSchema: Yup.object({
firstName: Yup.string().max(15, '至少要超過 15 個字').required('firstName 為必填'),
email: Yup.string().email('無效的 Email').required('email 為必填'),
}),
onSubmit: (values) => {
console.log(JSON.stringify(values, null, 2));
},
});
return (
<form onSubmit={formik.handleSubmit}>
<div>
<label htmlFor="email">Email Address</label>
<input
id="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}
</div>
<button type="submit">Submit</button>
</form>
);
};
檢驗表單是否碰過
使用 Formik 中的 touched
物件,搭配在 input
欄位上的 onBlur
事件:
const SignupForm = () => {
const formik = useFormik({
/* ... */
});
return (
<form onSubmit={formik.handleSubmit}>
<div>
<label htmlFor="email">Email Address</label>
<input
id="email"
name="email"
type="email"
onChange={formik.handleChange}
value={formik.values.email}
// STEP 1: 使用 onBlur 搭配 formik.handleBlur
onBlur={formik.handleBlur}
/>
{/* STEP 2: 取得 touched 物件的值 */}
{formik.touched.firstName && formik.errors.firstName ? (
<div>{formik.errors.firstName}</div>
) : null}
</div>
<button type="submit">Submit</button>
</form>
);
};
簡化程式碼 - getFieldProps()
在上面的範例中可看到需要自己在 <input>
中寫 onChange
, onBlur
, value
等等,formik
提供一個名為 getFiledProps
的 helper 可以簡化這些程式碼:
import React from 'react';
import { useFormik } from 'formik';
import * as Yup from 'yup';
const SignupForm = () => {
const formik = useFormik({
/* ... */
});
return (
<form onSubmit={formik.handleSubmit}>
<div>
<label htmlFor="email">Email Address</label>
{/**
* STEP 1:使用 `formik.getFieldProps()` 可以省去要自己註冊事件的時間
**/}
<input name="email" {...formik.getFieldProps('email')} />
{formik.touched.email && formik.errors.email ? <div>{formik.errors.email}</div> : null}
</div>
<button type="submit">Submit</button>
</form>
);
};
使用 Formik 元件,進一步簡化程式碼
在 Formik 中進一步提供 <Formik>
這個元件來簡化表單的程式架構,它利用了 React Context API 來實作,使用 <Formik />
元件後,內部可以在使用 <Form />
, <Field />
和 <ErrorMessage />
:
先將 useFormik
改成用 Formik
:
進一步搭配 <Form>
, <Field>
, ErrorMessage
,程式碼變得更簡潔:
API
Formik
<Formik />
@ Formik API
<Formik />
中上可用屬性和對應的 helper 可參考 API 文件:
/* demo use for array of objects with formik */
import React from 'react';
import { Formik, Field, Form } from 'formik';
const Invitation = () => (
<div>
<Formik
initialValues={initialValues}
onSubmit={(values) => {
/* values to submit ... */
}}
validationSchema={
{
/* validation rules here */
}
}
>
{(formik) => <FormikFormComponent />}
</Formik>
</div>
);
export default Invitation;
Form and Field
<Field />
@ Formik API Docs
<Form className="col s-12">
{/* 添加 label */}
<label htmlFor="firstName">First Name</label>
<Field name="firstName" placeholder="Jane" />
{/* children 可以是 function:如果需要自己控制 input */}
<Field name="user.name" type="text">
{({ field, form, meta }) => <input {...field} type="text" placeholder="Jane Doe" />}
</Field>
{/* children 也可以是 JSX */}
<Field name="color" as="select" placeholder="Favorite Color">
<option value="red">Red</option>
<option value="green">Green</option>
<option value="blue">Blue</option>
</Field>
<Field name="user.email" type="email" placeholder="jane@example.com" />
</Form>
FieldArray
keywords: push
, remove
從原本的
<Form>
改成帶有<FieldArray>
的 form。
如果資料是 array of objects 可以使用 <FieldArray>
,它會多了 push
和 remove
的方法,方便我們進行資料的新增和刪除:
<Form>
<FieldArray name="friends">{({ push, remove }) => <FormikFormComponent />}</FieldArray>
</Form>