React Hook Formに検証ライブラリYupを導入する

# Yupの導入

- 以下のフォームをベースにYupを導入する
- yupを導入することで、入力要素と検証が分離され、コードの見通しが良くなる。

https://qiita.com/tseno/items/83b58a35717861980ff0

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

- yupとyupResolverをインストールする
- yupResolver は、React Hook Form と Yup を統合するためのユーティリティ関数

```sh
> npm install @hookform/resolvers yup
# または
> yarn add @hookform/resolvers yup
```

```javascript
import { useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import * as yup from "yup";
```

## 2. バリデーションを書く

- 基本
  - `yup.object({フィールド名: yup.string().required('必須エラーメッセージ')   })` の形式
- labelを使う場合
  - `yup.object({フィールド名: yup.string().label('フィールド名').required('${label}必須エラーメッセージ')   })`


```javascript
const schema = yup.object({
  name: yup
    .string()
    .label("名前")
    .required("${label}は必須入力です")
    .max(20, "${label}は${max}文字以内で入力してください。"),
  country: yup.string().label("国籍").required("${label}は必須入力です。"),
  gender: yup.string().label("性別").required("${label}は必須入力です。"),
  email: yup
    .string()
    .label("メールアドレス")
    .required("${label}は必須入力です")
    .email("${label}の形式が不正です。"),
  agree: yup
    .boolean()
    .oneOf([true], "利用規約に同意してください。")
    .label("同意"),
  memo: yup
    .string()
    .label("備考")
    .required("${label}は必須入力です")
    .min(10, "${label}は${min}文字以上で入力してください。"),
});
```

## 3. useFormの初期化に、`resolver: yupResolver(schema),` を追加する

```jsx
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm({
    defaultValues,
    // Yupに検証を委ねる
    resolver: yupResolver(schema),
  });
```



## 4. 入力要素は `...register('フィールド名')` の形式（バリデーションを外す）

```jsx
<input id="name" type="text" {...register("name")} />
```

# ソース

```javascript
import { useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import * as yup from "yup";

const schema = yup.object({
  name: yup
    .string()
    .label("名前")
    .required("${label}は必須入力です")
    .max(20, "${label}は${max}文字以内で入力してください。"),
  country: yup.string().label("国籍").required("${label}は必須入力です。"),
  gender: yup.string().label("性別").required("${label}は必須入力です。"),
  email: yup
    .string()
    .label("メールアドレス")
    .required("${label}は必須入力です")
    .email("${label}の形式が不正です。"),
  agree: yup
    .boolean()
    .oneOf([true], "利用規約に同意してください。")
    .label("同意"),
  memo: yup
    .string()
    .label("備考")
    .required("${label}は必須入力です")
    .min(10, "${label}は${min}文字以上で入力してください。"),
});

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

  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm({
    defaultValues,
    // Yupに検証を委ねる
    resolver: yupResolver(schema),
  });

  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")} />
          <div>{errors.name?.message}</div>
        </div>
        <div>
          <label htmlFor="gender">性別：</label>
          <div>
            <label>
              <input type="radio" value="male" {...register("gender")} />
              男性
            </label>
            <label>
              <input type="radio" value="female" {...register("gender")} />
              女性
            </label>
          </div>
          <div>{errors.gender?.message}</div>
        </div>
        <div>
          <label htmlFor="country">国籍：</label>
          <select id="country" {...register("country")}>
            <option value="">選択してください</option>
            <option value="japan">日本</option>
            <option value="usa">アメリカ</option>
            <option value="china">中国</option>
            <option value="other">その他</option>
          </select>
          <div>{errors.country?.message}</div>
        </div>
        <div>
          <label htmlFor="email">メールアドレス：</label>
          <input id="email" type="email" {...register("email")} />
          <div>{errors.email?.message}</div>
        </div>
        <div>
          <label htmlFor="agree">
            <input id="agree" type="checkbox" {...register("agree")} />
            利用規約に同意する
          </label>
          <div>{errors.agree?.message}</div>
        </div>
        <div>
          <label htmlFor="memo">備考：</label>
          <textarea id="memo" {...register("memo")} />
          <div>{errors.memo?.message}</div>
        </div>
        <div>
          <button type="submit">送信</button>
        </div>
      </form>
    </div>
  );
}
```

# 動作画面

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