ReactのフォームライブラリでFormikをやめてreact-hook-formを使いたい
概要
業務でFormikを使ってフォームを何個か作っていたんですが、
Formikはパフォーマンスが悪い、react-hook-formを使うべきといった記事を見かけたので
実際に試してみたかったので、ついでに 使ってみた の記事も書きました。
npmトレンドを見ると、どちらもシェアを伸ばしてますが、
react-hook-form
の方が人気になってきているようです。
Formik
まずはFormikから使ってみます。
インストール
yarn add formik yup
yarn add -D @types/yup
formikとバリデーションスキーマ定義用のyupをインストール
コードを書いてみる
bbb
と ccc
の 文字数と必須入力をチェックするフォームです。
フォームのレンダリングの回数を見たかったので、 console.log('renderd')
を置いてます。
const initialValues = {
aaa: '',
bbb: '',
ccc: '',
}
const formValidationSchema = yup.object().shape({
aaa: yup.string(),
bbb: yup.string().required('入力は必須です'),
ccc: yup.string().max(5, '5文字以内で入力してください').required('入力は必須です'),
})
const FormikTestForm = () => {
const handleFormSubmit = (data) => {}
const { values, errors, touched, handleBlur, handleChange, handleSubmit } =
useFormik({
onSubmit: handleFormSubmit,
initialValues,
validationSchema: formValidationSchema,
})
console.log('renderd') //レンダリングの確認用
return (
<form onSubmit={handleSubmit}>
Formik
<div className="mb-4">
<input
name="aaa"
className="p-2"
placeholder="aaa"
type="text"
onBlur={handleBlur}
onChange={handleChange}
value={values.aaa}
/>
</div>
<div className="mb-4">
<input
className="p-2"
type="text"
placeholder="bbb"
name="bbb"
onBlur={handleBlur}
onChange={handleChange}
value={values.bbb}
/>
{touched.bbb && errors.bbb && (
<p className="text-red-400">{errors.bbb}</p>
)}
</div>
<div className="mb-4">
<input
name="ccc"
className="p-2"
placeholder="ccc"
value={values.ccc}
onBlur={handleBlur}
onChange={handleChange}
/>
{touched.ccc && errors.ccc && (
<p className="text-red-400">{errors.ccc}</p>
)}
</div>
<button
type="submit"
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
>
送信
</button>
</form>
)
}
画面イメージ
各inputへの入力やフォーカスが外れる度に
フォーム全体が複数回再レンダリングされています。
確かにパフォーマンスが悪そうです。
react-hook-form
今度は react-hook-form
を使ってみます。
インストール
yarn add react-hook-form
react-hook-formをインストール
バリデースキーマを定義したかったら下記もインストール
@hookform/resolvers
コードを書いてみる
公式のサンプルを参考に、とりあえずの簡単なフォームを作成しました。
上記のFormik
で作ったフォームと同じく、bbb
と ccc
の 文字数と必須入力をチェックするフォームです。
レンダリングの回数を見たかったので、 console.log('renderd')
を置いてます。
type Inputs = {
aaa: string
bbb: string
ccc: string
}
const ReactHookFormTestForm = () => {
const {
register,
handleSubmit,
formState: { errors },
} = useForm<Inputs>()
const onSubmit = (data) => {
console.log(data)
}
console.log('renderd') //レンダリングの確認用
return (
<form onSubmit={handleSubmit(onSubmit)}>
React-Hook-Form
<div className="mb-4">
<input
className="p-2"
type="text"
placeholder="aaa"
{...register('aaa')}
/>
</div>
<div className="mb-4">
<input
className="p-2"
type="text"
placeholder="bbb"
{...register('bbb', { required: true })}
/>
{errors.bbb && <p className="text-red-400">入力は必須です。</p>}
</div>
<div className="mb-4">
<input
id="name"
className="p-2"
placeholder="ccc"
{...register('ccc', { required: true, maxLength: 5 })}
/>
{errors.ccc && errors.ccc.type === 'required' && (
<p className="text-red-400">入力は必須です</p>
)}
{errors.ccc && errors.ccc.type === 'maxLength' && (
<p className="text-red-400">5文字以内で入力してください。</p>
)}
</div>
<button
type="submit"
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
>
送信
</button>
</form>
)
画面イメージ
作成したフォームを使ってみました。
入力内容にエラーがあった時と、再入力でエラーが解消されたときにだけ再レンダリングしていますね。
必要最小限のレンダリングで、非常に効率が良さそうです。
補足
react-hook-form
はデフォルトでは、フォームがsubmitされた後にバリデーションのチェックを開始します。
Formik
react-hook-form
useForm
フックは引数に複数のoptionを渡せるのですが、その中で mode
オプションを
onChange
にするとFormik
と同じように submit される前でもバリデーションのチェックを行うことができるみたいです。
useForm({
mode: 'onChange',
})
公式ではパフォーマンスに影響があるので注意、と書かれていますが、
それでもFormik
と比べるとパフォーマンスは良さそうですね。
終わりに
ブラウザの perfomance タブなどでのちゃんとした検証ではないが、
巨大なフォームだったり、底辺の端末とかだと、より違いが分かるかもしれない。
現状は新規で導入する場合、 よりパフォーマンスが良いreact-hook-form を選ぶべき。
参考
日本語版
Discussion