React Hook Form　基本をマスター＆活用する

実務2週間（React歴も2週間）の私が、React Hook Formの公式ドキュメントを読む前に欲しかったなと思うマニュアルです。
一番シンプルな実装を5stepで説明します。

また、分からないなりに調べ漁った結果学んだ、あんまり知られていないみたいだけど使える活用例もご紹介したいと思います。

↓このサンプルコードにも盛り込んでいるので、実際に動かしてみていただければと思います。
https://codesandbox.io/s/react-hook-form-example-kuj6w?file=/src/App.tsx


## React Hook Formとは

React16.8.0から導入された、hooksの仕組みを利用したライブラリ。公式ドキュメントでは「高性能で柔軟かつ拡張可能な使いやすいフォームバリデーションライブラリ」と掲げられています。

他のReactのフォームライブラリであるFormikやredux formと比較して、公式ドキュメントでは以下のようなメリットが挙げられています。

- 非制御コンポーネント（Uncontrolled Components）を採用しており、input要素をフォーム全体の再レンダリングなしに監視して変更できるので、レンダリング回数を減らすことが出来る
- state管理などのコード記述量を減らすことが出来る
- パッケージが軽量

この通りReact Hook Formは、簡単にバリデーションが実装できたり、レンダリング回数を削減できたり、フォーム管理のための便利な機能を提供してくれるライブラリです。

## 基本的な実装手順
React Hook Formの基本的な実装に必要なのは、5つの手順のみです。

## ①ライブラリのインストール

npmもしくはyarnコマンドでインストールします。

```bash
$ npm install react-hook-form
$ yarn add react-hook-form
```

## ②useFormの宣言

- ファイルの先頭でインポートをしておき、関数コンポーネント内でuseFormの初期化の宣言をします。
- useForm()から、使いたいAPIを分割代入の書き方で受け取ります。以下の例の通り、register, handleSubmit, formStateの中のerrors という3つは基本の実装に必要です。
- useFormの引数には、オブジェクト形式でオプションの設定を指定します。(以下の例では、criteriaMode, defaultValues の2つを設定しています。）

※useFormのオプション引数、useForm()から受け取れるAPIについては後ほど説明します。

```jsx
// JavaScript

import { useForm } from 'react'

export const Form = () => {

// useFormの初期化
  const { 
    register, 
    handleSubmit, 
    formState: { errors }
  } = useForm({
    criteriaMode: "all"      // 発生した全てのエラーを受け取る
    defaultValues: {         // 初回レンダリング時のフォームのデフォルト値
      name: '',
      email: ''
    }
});
```

※TypeScriptの場合は予め、useFormに対して型を渡しておきます。

```jsx
// TypeScript

type FormData = {
    name: string;
    email: string;
};

const {
  register,
  handleSubmit,
  watch,
  formState: { errors, isDirty, isSubmitting }
} = useForm<FormData>({
  criteriaMode: "all" ,     // 発生した全てのエラーを受け取る
  defaultValues: {         // 初回レンダリング時のフォームのデフォルト値
      name: '',
      email: ''
  }
});
```

### useFormへ渡せるオプション引数

任意の引数を渡すことで、フォーム全体のバリデーションのタイミングを制御したり、初回レンダリング時のデフォルト値を設定することができます。

設定することのできる引数を紹介していきます。

- mode…バリデーションが実行されるタイミングを設定する。
    
    （デフォルト：onSubmit）
    
    - onSubmit：送信イベントでトリガーされる。
    - onBlur：フォームからフォーカスが離れたときにトリガーされる。
    - onTouched：フォームからフォーカスが離れたときにトリガーされる。
    - onChange：入力の度にトリガーされる。※但し公式ドキュメントに「Warning: this often comes with a significant impact on performance.」とあり、非推奨になっている。
- reValidateMode…フォーム送信後にエラーがある入力を再検証するタイミングを設定する。（デフォルト：onChange）
    - onChange：入力の度にトリガーされる。
    - onBlur：フォームからフォーカスが離れたときにトリガーされる。
    - onSubmit：送信イベントでトリガーされる。
- criteriaMode…最初に発生したエラーのみを収集するか、全てのエラーを収集するか設定する。（デフォルト：firstErrorが採用される。）
    - firstError：最初に発生したエラーのみ収集する。
    - all：全てのエラーを収集する。
    
- defaultValues…初回レンダリング時のフォームの初期値を、オブジェクト形式で設定する。

※この他に指定できるオプション引数は公式ドキュメントを参照ください。[API ドキュメント](https://react-hook-form.com/jp/api#useForm)

## ③register関数を使ってフォーム要素を登録
- **register**…React Hook FormのAPIのひとつで、input要素やselect要素をref（参照）に登録し、バリデーションルールを設定するため関数。

簡単に言うと、管理したいフォーム要素（inputやselectなど）のタグ内にregister関数を使った記述をすることで、その要素をReact Hook Formで管理できるようになります。

第1引数：参照の名前を登録する。（一意かつ必須）
第2引数：バリデーションのルールをオブジェクト形式で渡す。

※使用できるバリデーションを以下にいくつか紹介します。その他のバリデーションは公式ドキュメントを参照ください。[useForm - register](https://react-hook-form.com/api/useform/register)

```jsx
// 基本形（参照名の登録のみ）
<input {...register("name")} />

// 検証ルールを設定
<input
  {...register("name", {
    required: true,        // 必須入力
    maxLength: 10          // 最長文字数
  })}
/>

// 検証ルールとエラーメッセージを設定
<input
  {...register("name", {
    required: '必須項目です。'
/>

// 検証ルールとエラーメッセージを設定
<input
  {...register("name", {
      maxLength : {
        value: 10,
        message: '10文字以下で指定してください。'
      }
  })}
/>

// メールアドレスの検証例
{...register('email', {
	  required: '必須入力です。',
	  pattern: {
	    value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
	    message: '正しいメールアドレスの形式で入力してください。',
	  },
	  validate: (value) =>
	    value === getValues('email_confirmation') ||
	    'メールアドレスとメールアドレス確認が違います。',
 })}
```

【補足説明】

- validate: では、バリデーションのコールバック関数を指定できる。
- getValuesでは、引数に参照名を指定すると、その参照の値を取得できる。
    ※但しgetValuesは指定した参照の値を常に監視しているわけではない。上記の例でいえば、`validate: (value) => value === getValues('email_confirmation')` の記述をしたinput要素のイベントの発火タイミングで値を取得する。常に値を監視したい場合は、後述のwatchを使う。
    

## ④handleSubmitでフォーム送信処理を操る

- **handleSubumit**…フォーム送信時の処理をハンドリングする。

handleSubmit関数の引数には以下の2つを指定できます。
    
第1引数：フォームの検証が成功した時のコールバック関数→フォームの入力値をコールバック関数の引数で受け取る
第2引数：フォームの検証が失敗した時のコールバック関数
    →エラー内容をコールバック関数の引数で受け取る
※受け取るデータはオブジェクト形式
    

```jsx
export const Form = () => {
	const handleOnSubmit = (values) => console.log(values);
  const handleOnError = (errors) => console.log(errors);

	return (
		<form onSubmit={handleSubmit(handleOnSubmit, handleOnError)}>
			<input type="submit" />
		</form>

	   {// <form>タグではなく<button>タグへの指定も可能 }
        <button onClick={handleSubmit(handleOnSubmit)}>送信</button>

```

## ⑤エラーメッセージを表示する

バリデーションに引っかかった時のエラーは、formStateというAPIのerrorsに保管されます。

- **formState**…フォームに関する情報をオブジェクト形式で保持する。
    - **errrors**：バリデーションエラーのあるフォーム要素を収集したオブジェクトを返す。

```jsx
<input
  type="text"
  {...register("name", {
    required: "* 必須項目です。",
  })}
/>

// "name"という参照名でregisterに登録したinput要素にエラーがあれば、
// そのエラーメッセージを表示する
{!!formState.errors.name && <p>{formState.errors.name.message}</p>}
```

### サンプルコード
以上で基本的な実装は完了です。
一旦ここまでの内容のサンプルコードを掲載します。

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

export default function App() {
  type FormData = {
    name: string;
    email: string;
};

const {
  register,
  handleSubmit,
  formState: { errors }
} = useForm<FormData>({
  criteriaMode: "all",
  defaultValues: {
      name: '',
      email: ''
  }
});

const handleOnSubmit = (data: FormData) => console.log(data);
const handleOnError = (errors: any) => console.log(errors);

return (
  <form onSubmit={handleSubmit(handleOnSubmit, handleOnError)}>
    <div>
      <label htmlFor="name">名前: </label>
      <input
        {...register("name", {
          required: "名前を入力してください"
        })}
        type="text"
      />
      {errors.name && <p style={{ color: "red" }}>{errors.name.message}</p>}
    </div>

    <div>
      <label htmlFor="name">メールアドレス: </label>
      <input
        {...register("email", {
          required: "メールアドレスを入力してください",
　         pattern: {
            value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
            message: "正しいメールアドレスの形式で入力してください。"
          }
        })}
        type="text"
      />
      {errors.email && <p style={{ color: "red" }}>{errors.email.message}</p>}
    </div>
      
    <button type="submit">送信</button>
  </form>
);
}
```



## ⑥その他のAPIと活用例
最後に、知っていると便利な活用例をご紹介します。


- **watch**…指定したnameを持つフォーム要素の値を監視し、その値を取得する。
    
    ```php
    // emailconfirmationが変更される度にその値を取得する
    const email = watch('email')
    ```
    
    **watchの活用例**
    
    特定のフォーム要素を監視しておき、その入力値に対応して画面表示を変えることができる。
    
    ```php
    const likeFruits: string = watch("likeFruits");
    
    // 中略
    
    	<p>果物は好きですか？</p>
    	
    	<div>
    	  <input {...register("likeFruits")} type="radio" value="yes" />
    	  はい
    	  <input {...register("likeFruits")} type="radio" value="no" />
    	  いいえ
    	</div>
    	
    	{likeFruits === "yes" && (
    	  <div>
    	    <label htmlFor="favorite">1番好きな果物は？</label>
    	    <select {...register("favolite")}>
    	      <option value="apple">りんご</option>
    	      <option value="orange">みかん</option>
    	      <option value="strawberry">いちご</option>
    	    </select>
    	  </div>
    	)}
    ```
    

- **isDirty**…ユーザーが何らかの入力操作をしたらtureを返す。
- **isSubmitting**…フォームの送信中はtrue、送信が完了するとfalseを返す。
    
    isDirty, isSubmittingは、errorsと同じくformStateの持つ情報です。
    
    ※formStateの持つその他の情報については公式ドキュメント参照
    
    [API ドキュメント](https://react-hook-form.com/jp/api#formState)
    

- **isDirty, isSubmittingの活用例**
    
    booleanを返してくれるので、HTML要素のdisableに指定すれば、ユーザーが何らかの入力を行うまでは送信ボタンを無効にする・フォーム送信中は送信ボタンを無効にするといった使い方が出来ます。
    

```php
<button type="submit" disabled={!isDirty || isSubmitting}>
  送信
</button>
```


## 参考記事
https://react-hook-form.com/jp/

https://off.tokyo/blog/react-ref/

https://tech.stmn.co.jp/entry/2021/04/23/091310

https://www.youtube.com/watch?v=vNrUPktDT7o