React Hook Form + zod を使ってフォームを作成する

# はじめに
この記事は**React Hook Form** と **zod** を使ったフォームの作り方の備忘録です。
間違っている部分がありましたらコメントで教えてもらえると嬉しいです。

# 使うライブラリについて
**React Hook Form** は 入力された値を取得するために使います。
**zod** は 入力された値のバリデーションのために使います。
本来 **React Hook Form** のみでバリデーション実装も可能なのですが
**zod** をバリデーションとして使うことで
バリデーション条件を **JSX** から分離して分かりやすくします。

# インストール
```
npm install react-hook-form
npm install @hookform/resolvers zod
```
## React Hook Form を使ったフォームの作り方
まず **React Hook Form** だけを使ってフォームの挙動を確認します。

```jsx
import { useForm } from 'react-hook-form';

function App() {
  const { register, handleSubmit } = useForm();
  const onSubmit = (data) => console.log(data);

  return (
    <div className="App">
    <form onSubmit={handleSubmit(onSubmit)}>
      <div>
        <label htmlFor="email">Email</label>
        <input id="email" {...register('email')} />
      </div>
      <div>
        <label htmlFor="password">Password</label>
        <input id="password" {...register('password')} type="password" />
      </div>
      <button type="submit">ログイン</button>
    </form>
  </div>
  );
}
```
**input要素** に渡した **register関数の引数** が **キーになるオブジェクト** が
**handleSubmit関数** の第1引数に渡した **callback関数** の引数に渡ります。
この例の場合、 **onSubmit関数** の引数 **data** に渡ります。

例えば
**email.input** の入力内容は **data.email** に
**password.input** の入力内容は **data.password** に 代入されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3348923/940832ff-7b99-c272-c1f9-242ee5a281af.png)


## zod でバリデーションを実装する
次に **zod** を **React Hook Form** に適用してバリデーションを実装します。
```jsx
import { useForm } from 'react-hook-form';
import { zodResolver } from "@hookform/resolvers/zod"
import * as z from "zod"

const schema = z.object({
  email: z.string().email().min(1),
  password: z.string().min(1)
});

function App() {
  const { register, handleSubmit, formState: { errors } } = useForm({
    resolver: zodResolver(schema)
  });
  const onSubmit = (data) => console.log(data);
  
  console.log(errors);

  return (
    <div className="App">
    <form onSubmit={handleSubmit(onSubmit)}>
      <div>
        <label htmlFor="email">Email</label>
        <input id="email" {...register('email')} />
      </div>
      <div>
        <label htmlFor="password">Password</label>
        <input id="password" {...register('password')} type="password" />
      </div>
      <button type="submit">ログイン</button>
    </form>
  </div>
  );
}

```
**React Hook Form** でフォームの挙動を確認した時との違いは

1.**変数schema** にバリデーションするルールを記述している
2.**useFormの引数** にzodのバリデーションを追加している
3.**useFormの戻り値** から **formState.errors** を分割代入で取得している

これら3つのうち 1と2 でバリデーションを追加しています。

バリデーションのエラーは分割代入で取得した **formState.errors** に代入されます。
**formState.erros** に代入されるオブジェクトはこのような形になっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3348923/a5a233aa-6e55-7983-0679-ce0f96adad9d.png)

なので **formState.erros** を **JSX** 内で使うことでバリデーションエラーを描画出来ます。

```jsx
import { useForm } from 'react-hook-form';
import { zodResolver } from "@hookform/resolvers/zod"
import * as z from "zod"

const schema = z.object({
  email: z.string().email().min(1),
  password: z.string().min(1)
});

function App() {
  const { register, handleSubmit, formState: { errors } } = useForm({
    resolver: zodResolver(schema)
  });
  const onSubmit = (data) => console.log(data);
  
  console.log(errors);

  return (
    <div className="App">
    <form onSubmit={handleSubmit(onSubmit)}>
      <div>
        <label htmlFor="email">Email</label>
        <input id="email" {...register('email')} />
        <p>{errors.email?.message}</p>
      </div>
      <div>
        <label htmlFor="password">Password</label>
        <input id="password" {...register('password')} type="password" />
        <p>{errors.password?.message}</p>
      </div>
      <button type="submit">ログイン</button>
    </form>
  </div>
  );
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3348923/e9514ce2-13fc-9e25-6581-5ad35b76a1d7.png)

## バリデーションエラーの文言を変更する
```jsx
import { useForm } from 'react-hook-form';
import { zodResolver } from "@hookform/resolvers/zod"
import * as z from "zod"

const schema = z.object({
  email: z.string().email({ message: "メールアドレスの形式で入力してください。"}).min(1, { message: "1文字以上で入力する必要があります。"}),
  password: z.string().min(1, { message: "1文字以上で入力する必要があります。"})
});

function App() {
  const { register, handleSubmit, formState: { errors } } = useForm({
    resolver: zodResolver(schema)
  });
  const onSubmit = (data) => console.log(data);
  
  console.log(errors);

  return (
    <div className="App">
    <form onSubmit={handleSubmit(onSubmit)}>
      <div>
        <label htmlFor="email">Email</label>
        <input id="email" {...register('email')} />
        <p>{errors.email?.message}</p>
      </div>
      <div>
        <label htmlFor="password">Password</label>
        <input id="password" {...register('password')} type="password" />
        <p>{errors.password?.message}</p>
      </div>
      <button type="submit">ログイン</button>
    </form>
  </div>
  );
}
```
**変数schema** のバリデーションルールに変更を加えると **任意の文言** に変更出来ます。
詳しくは **zod** の公式ドキュメントに乗っています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3348923/f82a4fd1-1dbd-0436-3d51-adffba3ab016.png)

### さいごに
学習させていただいた先・ライブラリの公式ドキュメントです。

https://reffect.co.jp/react/react-hook-form#Zod

https://zod.dev/

https://react-hook-form.com/docs/useform/formstate