React Hook Formで検証機能の実装

# React Hook Formの基本機能

## 1. インストール＆インポート

```sh
> npm install react-hook-form
# または
> yarn add react-hook-form
```

```javascript
import { useForm } from "react-hook-form";
```

## 2. フォームの初期化

- defaultValuesで初期値の設定
- useFormでフォームの初期化
    - register 入力要素にハンドラーなどを登録する
    - handleSubmit サブミット時に使用する
    - formState: { errors } エラー状態を取得する
    - defaultValues 初期値を設定する

```javascript
  const defaultValues = {
    name: "",
    email: "",
    gender: "male",
    country: "japan",
    agree: false,
    memo: "",
  };
  
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm({
    defaultValues,
  });
```

## 3. FormのonSubmit設定

- `onSubmit=handleSubmit(onsubmit, onerror)`
    - `noValidate` ブラウザによるデフォルトのフォーム入力検証を無効
- `onsubmit` 検証成功時の処理
- `onerror` 検証失敗時の処理

```javascript
<form onSubmit={handleSubmit(onsubmit, onerror)} noValidate>
  {/* 入力要素... */}
  <button type="submit">送信</button>
</form>
```

```javascript
const onsubmit = (data) => console.log(data);
const onerror = (err) => console.log(err);
```

## 4. 入力要素にregisterをスプレッド演算子で展開する(フィールド名, 検証ルールなど)

- 基本  register(フィールド名)をinputタグに入れる
    - `...register("name")`
- バリデーション
    - `{required: "必須メッセージ", maxLength: {value: 20, message: "文字数メッセージ"}}`
- エラー表示部分
    - `{errors.name && <div>{errors.name.message}</div>}`

```javascript
<div>
  <label htmlFor="name">名前：</label>
  <input
    id="name"
    type="text"
    // registerをスプレッド演算子で展開する(フィールド名, 検証ルールなど)
    {...register("name", {
      required: "名前は必須入力です。",
      maxLength: {
        value: 20,
        message: "名前は20文字以内にしてください",
      },
    })}
  />
  {errors.name && <div>{errors.name.message}</div>}
</div>
```

## ソース全体

```javascript
import { useForm } from "react-hook-form";

export default function FormBasic() {
  const defaultValues = {
    name: "",
    email: "",
    gender: "male",
    country: "japan",
    agree: false,
    memo: "",
  };

  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm({
    defaultValues,
  });

  const onSubmit = (data) => console.log(data);
  const onError = (errors) => console.log(errors);

  return (
    <div>
      <form onSubmit={handleSubmit(onSubmit, onError)} noValidate>
        <div>
          <label htmlFor="name">名前：</label>
          <input
            id="name"
            type="text"
            {...register("name", {
              required: "名前は必須入力です。",
              maxLength: {
                value: 20,
                message: "名前は20文字以内にしてください",
              },
            })}
          />
          {errors.name && <div role="alert">{errors.name.message}</div>}
        </div>
        <div>
          <label htmlFor="gender">性別：</label>
          <div>
            <label>
              <input
                type="radio"
                value="male"
                {...register("gender", {
                  required: "性別は必須です。",
                })}
              />
              男性
            </label>
            <label>
              <input
                type="radio"
                value="female"
                {...register("gender", {
                  required: "性別は必須です。",
                })}
              />
              女性
            </label>
          </div>
          {errors.gender && <div role="alert">{errors.gender.message}</div>}
        </div>
        <div>
          <label htmlFor="country">国籍：</label>
          <select
            id="country"
            {...register("country", {
              required: "国籍を選択してください。",
            })}
          >
            <option value="">選択してください</option>
            <option value="japan">日本</option>
            <option value="usa">アメリカ</option>
            <option value="china">中国</option>
            <option value="other">その他</option>
          </select>
          {errors.country && <div role="alert">{errors.country.message}</div>}
        </div>
        <div>
          <label htmlFor="email">メールアドレス：</label>
          <input
            id="email"
            type="email"
            {...register("email", {
              required: "メールアドレスは必須入力です。",
              pattern: {
                value: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
                message: "メールアドレスの形式が不正です。",
              },
            })}
          />
          {errors.email && <div role="alert">{errors.email.message}</div>}
        </div>
        <div>
          <label htmlFor="agree">
            <input
              id="agree"
              type="checkbox"
              {...register("agree", {
                required: "利用規約に同意してください。",
              })}
            />
            利用規約に同意する
          </label>
          {errors.agree && <div role="alert">{errors.agree.message}</div>}
        </div>
        <div>
          <label htmlFor="memo">備考：</label>
          <textarea
            id="memo"
            {...register("memo", {
              required: "備考は必須入力です。",
              minLength: {
                value: 10,
                message: "備考は10文字以上にしてください",
              },
            })}
          />
          {errors.memo && <div role="alert">{errors.memo.message}</div>}
        </div>
        <div>
          <button type="submit">送信</button>
        </div>
      </form>
    </div>
  );
}
```

## 動作画面

https://codesandbox.io/embed/fn7hr3?view=preview&module=%2Fsrc%2FFormBasic.js


## 続き

https://qiita.com/tseno/items/cb8e45c22669b227ab2c