はじめに
Reactでフォームを実装しようとすると、入力値の管理・バリデーション・送信中の状態・
エラーメッセージの表示など、地味に面倒な処理が一気に積み上がります。useState を
フィールドの数だけ並べ、onChange を1つずつ書き、気づけばフォーム本体よりも
状態管理のコードの方が長くなっていた、という経験がある方も多いのではないでしょうか。
Formikは、そんなReactのフォーム開発を「涙なし」で行えるようにするライブラリです。 公式サイトのキャッチコピーも "Build forms in React, without the tears 😭" となっており、 値の管理・バリデーション・送信処理を一貫したAPIでまとめて扱えます。
とはいえ、読むより触った方が早いと思います。入力するとリアルタイムでバリデーションが 走るサンプルを用意したので、先に挙動を見たい方はこちらからどうぞ。
Formikとは
Formikは、Jared Palmerらによって開発されているReact向けのフォームライブラリです。
values(入力値)、errors(エラー内容)、touched(操作済みかどうか)といった
フォームの状態をまとめて管理し、handleChange や handleSubmit のようなハンドラを
自動生成してくれます。React Native環境でも同じAPIで動作します。
主な特徴
- 状態管理を肩代わりしてくれる -
values/errors/touched/isSubmittingなどを 1つのオブジェクトで一元管理し、フィールドごとのuseStateが不要になる - 複数のAPIスタイルに対応 -
useFormikフック、<Formik>コンポーネント、<Field>/<Form>といった宣言的なコンポーネントを用途に応じて使い分けられる - バリデーションの選択肢が広い - Yupスキーマによる宣言的な検証と、
自前の
validate関数による命令的な検証のどちらにも対応 - 動的なフィールドに強い -
<FieldArray>を使えば、入力欄の増減が必要なフォームも 配列操作だけで実装できる
インストール
npmまたはyarnで導入します。バリデーションにYupを使う場合は合わせてインストールします。
npm install formik yup
yarn add formik yup
Formikのサンプルを動かす
以下は useFormik フックを使った、名前とメールアドレスを入力するフォームです。
handleChange と handleBlur をそのまま <input> に渡すだけで、値の更新と
「操作済みかどうか」の追跡がFormik側で行われます。バリデーションは validate 関数で
自前に書いており、errors と touched の両方が真のときだけエラーメッセージを出しています。
要点だけを抜き出すと、次のようになります。
const formik = useFormik({
initialValues: { name: '田中太郎', email: 'taro@example.com' },
validate: (values) => {
const errors = {}
if (!values.name) errors.name = '名前は必須です'
if (!/^\S+@\S+\.\S+$/.test(values.email)) errors.email = 'メール形式が不正です'
return errors
},
onSubmit: (values) => console.log(values),
})
<input
name="name"
value={formik.values.name}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
{formik.touched.name && formik.errors.name && <p>{formik.errors.name}</p>}
実際に動かせるものが下です。名前欄を空にしたり、メールアドレスから @ を消したりすると、
その場でエラーメッセージが表示されます。逆に正しい値に戻すとエラーが消え、
送信ボタンが押せる状態になります。
formik.values にフォームの現在値、formik.errors にバリデーション結果、
formik.touched にフィールドを触ったかどうかが入っているのがポイントです。
validate 関数の中身を書き換えれば、パスワードの最小文字数チェックなど、
好きなルールをすぐに追加できます。
基本的な使い方
Formikには大きく分けて3つの書き方があります。上のサンプルで使った useFormik フックは、
フォームの見た目を完全に自分でコントロールしたいときに向いています。
もう1つの代表的な書き方が <Formik> コンポーネント + <Field> / <Form> の組み合わせです。
<Field> は name を渡すだけで value と onChange を自動的に紐付けてくれるため、
入力欄が多いフォームほどコード量の差が出ます。
import { Formik, Field, Form, ErrorMessage } from 'formik'
function LoginForm() {
return (
<Formik
initialValues={{ email: '', password: '' }}
onSubmit={(values) => console.log(values)}
>
<Form>
<Field name="email" type="email" />
<ErrorMessage name="email" component="p" />
<Field name="password" type="password" />
<ErrorMessage name="password" component="p" />
<button type="submit">ログイン</button>
</Form>
</Formik>
)
}
initialValues に渡したキーがそのまま各 <Field name="..."> と対応する点はどちらの
書き方でも共通です。プロジェクトの好みや既存コードのスタイルに合わせて選べます。
実践的なユースケース
Yupスキーマでバリデーションルールを宣言する
validate 関数を手書きする代わりに、Yupのスキーマを validationSchema に渡す方法です。
必須チェックや文字数制限、パターンマッチのようなよくあるルールを、条件分岐を書かずに
宣言的に表現できます。Formikは内部で validationSchema.validate() を呼び出し、
結果を自動的に errors に反映します。
username を2文字以下にしたり、age を17に変更したりすると、Yupのバリデーションが
反応してエラーメッセージが出ます。Yup.string().min(3, ...) の数値や、
Yup.number().min(18, ...) の条件を書き換えるだけでルールを調整できる手軽さが分かります。
FieldArrayで入力欄を動的に増減する
連絡先やタグのように、ユーザーが自由に項目を追加・削除できるフォームは、
配列の useState を自前で管理すると意外と手間がかかります。Formikの <FieldArray> は
push / remove といったヘルパーを提供してくれるため、配列操作だけで実装できます。
「連絡先を追加」を押すたびに push('') が呼ばれて空の入力欄が増え、「削除」を押すと
remove(index) でその項目だけが配列から取り除かれます。values.emails の中身は
常にFormikの状態として保持されているため、送信時にそのまま配列として取り出せます。
自前のvalidate関数でルールを検証する
Yupを使わず、関数だけで完結させたい場合は validate に自分でロジックを書けます。
パスワードの強度チェックのように、複数の条件を組み合わせたい場合や、
Yupのスキーマ表現にこだわらず素早く書きたい場合に向いています。
validatePassword はただのJavaScript関数なので、if 文を増やすだけで
「記号を含む」「連続した数字を禁止する」といったルールも自由に追加できます。
Yupのスキーマ記法を覚えるほどではない、ちょっとしたチェックに向いた書き方です。
まとめ
Formikは、useFormik フックによる自由度の高い書き方と、<Formik> + <Field> による
宣言的な書き方の両方を提供し、Yupとの組み合わせでバリデーションルールも簡潔に表現できる
ライブラリです。<FieldArray> を使えば動的な入力欄も配列操作だけで済み、
フォームの実装にありがちな「状態管理の煩雑さ」から解放してくれます。
まずは useFormik から触ってみて、フォームが増えてきたら <Formik> + <Field> に
切り替える、という段階的な導入がしやすい点もFormikの使いやすさの1つです。