【React】フォームを使うなら、react-hook-formがいいよ

## はじめに

多くのサイトにはフォームが存在していると思います。
Qiitaなどのユーザー投稿型のサイトもそうですし、ブログなどの静的サイトであっても、問い合わせページなどではフォームがあったりするかと思います。

では、Reactでフォームを取り扱うときどうすればいいでしょうか？

従来通り、useStateやuseRefを用いてフォームのvalueを管理し、APIを叩く方法が真っ先に思いつくかと思います。

ただ、ステートの管理やエラーのハンドリングなどが大変になってきたりします。

そこで出てくるのがフォームライブラリ。

2022現在、Reactでは`react-hook-form`というのが人気だったりします。

今回はこの`react-hook-form`のお話

## react-hook-formとは？

上に書いたように、フォームのステートやエラーの管理をしてくれます。
ドキュメントが日本語であるというのもいいところですね。

https://react-hook-form.com/

今回は個人的にいいなと思うことをピックアップして話します。

### 基本

```tsx
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というエラーハンドリング用のコンポーネントを使えば、エラー状態をハンドリングして表示を切り替えて来れたりします。

```tsx
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: バリデーションが通っているか

なども返してくれます。

例えば、フォームに一度は触って、バリデーションが通らないと送信ボタンを押せないようにする。
みたいなロジックもすぐに実現可能です。

```tsx
const { formState: { isDirty, isValid } } = useForm();
return <button disabled={!isDirty || !isValid} />;
```

また、`dirtyFields`など他のAPIも提供されており、一つ一つのフォームの状態を柔軟にとってくることができます。

この辺りのステートを自分で管理しようとするとめちゃくちゃ大変なので助かりますね。

### Submit状態を管理してくれる

上に同じく、formStateという中に、`isSubmitted`や`isSubmitSuccessful`などが入ってます。

なので、フォーム送信が成功したタイミングでリダイレクトさせる。

みたいな処理も楽に書くことができます。

```tsx
const { push } = useRouter()
const { formState: { isSubmitSuccessful } } = useForm();

useEffect(()) => {
  if (isSubmitSuccessful) {
    push('/success')
  }
}, [isSubmitSuccessful]}
```

onSubmitの中に処理を詰め込まず、関心ごとを分離してコードを書けるのでとても良いですね。

他にも、フォーム送信後にフォーム内容をリセットしたいなどの場合も`resetFiled`というAPIがあるのでそれを使えばいけたりします。

### FormProvider

フォームのステートやエラーなどを配布するProviderと取り扱うhooksを提供してくれます。
大体はフォームを別コンポーネントで切り出したりするので、子コンポーネント側でフォームのエラー状態のハンドリングやステートが柔軟に取り出して来れるのはいいことですね

```tsx
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について書いてみました。

ぜひ色々公式ドキュメントをみながら、使ってみてください