はじめに
多くのサイトにはフォームが存在していると思います。
Qiitaなどのユーザー投稿型のサイトもそうですし、ブログなどの静的サイトであっても、問い合わせページなどではフォームがあったりするかと思います。
では、Reactでフォームを取り扱うときどうすればいいでしょうか?
従来通り、useStateやuseRefを用いてフォームのvalueを管理し、APIを叩く方法が真っ先に思いつくかと思います。
ただ、ステートの管理やエラーのハンドリングなどが大変になってきたりします。
そこで出てくるのがフォームライブラリ。
2022現在、Reactではreact-hook-form
というのが人気だったりします。
今回はこのreact-hook-form
のお話
react-hook-formとは?
上に書いたように、フォームのステートやエラーの管理をしてくれます。
ドキュメントが日本語であるというのもいいところですね。
今回は個人的にいいなと思うことをピックアップして話します。
基本
import { useState } from "react";
import { useForm } from "react-hook-form";
import Header from "./Header";
export function App() {
const { register, handleSubmit } = useForm();
const onSubmit = () => {
// api叩いたりする
}
return (
<form onSubmit={handleSubmit(onSubmit)}>
<Header />
<input {...register("firstName")} placeholder="First name" />
<select {...register("category", { required: true })}>
<option value="">Select...</option>
<option value="A">Option A</option>
<option value="B">Option B</option>
</select>
<textarea {...register("aboutYou")} placeholder="About you" />
<p>{data}</p>
<input type="submit" />
</form>
);
}
これだけだと、ステートの管理をしてくれるものぐらいにしか恩恵が感じられなさそうですが、他にも色々な機能を持っているので紹介していきます。
バリデーションとエラーハンドリング
フォームを登録する際にバリデーションロジックを組み込むことができます。
また、ErrorMessageというエラーハンドリング用のコンポーネントを使えば、エラー状態をハンドリングして表示を切り替えて来れたりします。
import React from "react";
import { useForm } from "react-hook-form";
import { ErrorMessage } from '@hookform/error-message';
export default function App() {
const { register, formState: { errors }, handleSubmit } = useForm();
const onSubmit = data => console.log(data);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register("singleErrorInput", { required: "This is required." })} />
<ErrorMessage errors={errors} name="singleErrorInput" />
<ErrorMessage
errors={errors}
name="singleErrorInput"
render={({ message }) => <p>{message}</p>}
/>
<input type="submit" />
</form>
);
}
dirtyやisValidなどのステート提供
useStateのhooksが返してくれるのは、errorオブジェクトだけではありません。
- isDirty: フォームに触れられたかどうか
- isValid: バリデーションが通っているか
なども返してくれます。
例えば、フォームに一度は触って、バリデーションが通らないと送信ボタンを押せないようにする。
みたいなロジックもすぐに実現可能です。
const { formState: { isDirty, isValid } } = useForm();
return <button disabled={!isDirty || !isValid} />;
また、dirtyFields
など他のAPIも提供されており、一つ一つのフォームの状態を柔軟にとってくることができます。
この辺りのステートを自分で管理しようとするとめちゃくちゃ大変なので助かりますね。
Submit状態を管理してくれる
上に同じく、formStateという中に、isSubmitted
やisSubmitSuccessful
などが入ってます。
なので、フォーム送信が成功したタイミングでリダイレクトさせる。
みたいな処理も楽に書くことができます。
const { push } = useRouter()
const { formState: { isSubmitSuccessful } } = useForm();
useEffect(()) => {
if (isSubmitSuccessful) {
push('/success')
}
}, [isSubmitSuccessful]}
onSubmitの中に処理を詰め込まず、関心ごとを分離してコードを書けるのでとても良いですね。
他にも、フォーム送信後にフォーム内容をリセットしたいなどの場合もresetFiled
というAPIがあるのでそれを使えばいけたりします。
FormProvider
フォームのステートやエラーなどを配布するProviderと取り扱うhooksを提供してくれます。
大体はフォームを別コンポーネントで切り出したりするので、子コンポーネント側でフォームのエラー状態のハンドリングやステートが柔軟に取り出して来れるのはいいことですね
import React from "react";
import { useForm, FormProvider, useFormContext } from "react-hook-form";
export default function App() {
const methods = useForm();
const onSubmit = data => console.log(data);
return (
<FormProvider {...methods} > // pass all methods into the context
<form onSubmit={methods.handleSubmit(onSubmit)}>
<NestedInput />
<input type="submit" />
</form>
</FormProvider>
);
}
function NestedInput() {
const { register } = useFormContext(); // retrieve all hook methods
return <input {...register("test")} />;
}
まとめ
まだまだ紹介できていないこともありますが、個人的にいいなと思うことをピックアップしてreact-hook-formについて書いてみました。
ぜひ色々公式ドキュメントをみながら、使ってみてください