とりあえずreact-hook-form

# はじめに
react系のフォームバリデーションライブラリ[react-hook-form](https://react-hook-form.com/)のざっくり使い方です。


公式のドキュメントでは、

- 超軽量なパッケージ
- 再レンダリングを最小に押さえて、マウントの高速化
- フォームの値がローカル管理される為、他パッケージに依存しない

等々の利点が挙げられている。

# formikとの比較
## download
<img width="1112" alt="スクリーンショット 2020-01-16 0.49.24.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173323/95ab13b5-29f0-98a3-f2e4-19097b05fcec.png">
https://www.npmtrends.com/redux-form-vs-formik-vs-react-hook-form

## size
### [formik](https://bundlephobia.com/result?p=react-hook-form@4.5.2)
<img width="1145" alt="スクリーンショット 2020-01-16 0.54.11.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173323/1aa2018a-9b48-457a-faa9-10b9ced4ab2a.png">

### [react-hook-form](https://bundlephobia.com/result?p=react-hook-form@4.5.2)
<img width="1105" alt="スクリーンショット 2020-01-16 0.54.30.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173323/96973541-0d5d-0d37-af62-463928154b1b.png">


# API
## useForm
useFormでいろいろなapiを受け取れる

```js:useFormのoption(default)
const { register } = useForm({
  mode: 'onSubmit', // | 'onBlur' | 'onChange'
  reValidateMode: 'onChange', // onBlur | onSubmit 再度バリデーションされるタイミング
  defaultValues: {},
  validationSchema: {}, // スキーマレベルで Yup を使用してフォームバリデーションルールを適用
  validateCriteriaMode: "firstErrorDetected",
  submitFocusError: true, // エラーのある最初のフィールドがフォーカスされる
  nativeValidation: false, // ブラウザバリデーションの活用
})
```

## register
input/selectのRefとバリデーションルールをreact-hook-formに登録する

```jsx
<input name="form1_1" defaultValue="test" ref={register} />
```

registerでバリデーションをかけられる。下記のコードではrequired, minLengthを5にせっていしている。
'1_2は必須です', '5桁以上必要です'はエラーメッセージ。

```jsx
<input
  name="form1_2"
  ref={
    register({
      required: '1_2は必須です',
      minLength : {
        value: 5,
        message: '5桁以上必要です'
      }
    })
  }
/>
```


フィールドフォームネストして扱うこともできる

|name|output|
|:--|:--|
|name="firstName"|{ firstName: 'value' }|
|name="firstName[0]"| { firstName: [ 'value' ] }|
|name="name.firstName"|{ name: { firstName: 'value' } }|
|name="name.firstName[0]"|{ name: { firstName: [ 'value' ] } }|

## errors
errorsオブジェクトにはフォーム内の各フィールドのエラーオブジェクト。
react-hook-formにはエラーメッセージ用の表示にErrorMessageコンポーネントも用意されている。

```jsx
import { useForm, ErrorMessage } from 'react-hook-form'
// ~~
  <ErrorMessage errors={errors} name="form1_2" />
// ~~
```

## watch
指定されたnameのinputを監視して、その値を返す。

> defaultValue が定義されていない場合、watch の初回のレンダリングは register の前に呼び出されるため undefined を返しますが、 第2引数として defaultValue を設定して値を返すことができます。

> ただし、引数として useForm で defaultValues が初期化された場合、 初回のレンダリングは defaultValues で指定された値を返します。

```jsx
<p className="form1-watch-text">watch output: {watch('form1_1')}</p>
```


## handleSubmit
フォームバリデーションを通るとデータを渡す。

```jsx
// ~~
const onSubmit = (data: Object) => { console.table(data) };
// ~~
<input type="submit" />
// ~~
```

```js:async関数も渡すことができる
handleSubmit(async (data) => await fetchAPI(data))
```

![form1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173323/36f3a869-5170-0fd4-b58f-cacf52e0420c.gif)

```jsx:Form1
import React from 'react';
import { useForm, ErrorMessage } from 'react-hook-form'

export default function Form1() {
  const { register, handleSubmit, watch, errors } = useForm({  validateCriteriaMode: 'all' });
  const onSubmit = (data: Object) => { console.table(data) };

  return (
    <div className="form form1">
      <h1>Form1</h1>
      <form onSubmit={handleSubmit(onSubmit)}>
        <div className="form-section">
          <span>1_1: </span>
          <input name="form1_1" defaultValue="test" ref={register} />
          <span className="sub-text">*watched</span>
        </div>
        <div className="form-section">
          <span>1_2: </span>
          <input
            name="form1_2"
            ref={
              register({
                required: '1_2は必須です',
                minLength : {
                  value: 5,
                  message: '5桁以上必要です'
                }
              })
            }
          />
          <span className="sub-text">*required</span>
        </div>
        <div className="form1-watch">
          <p className="form1-watch-text">watch output: {watch('form1_1')}</p>
        </div>
        <div className="errors">
          <ErrorMessage errors={errors} name="form1_2" />
        </div>
        <input type="submit" />
      </form>
    </div>
  )
};

```
## controller
Controllerコンポーネント(UIコンポーネントライブラリと併せて使用するコンポーネント)用。
コンポーネント登録するためのメソッドが含まれている。
以下ではmaterial-uiを使用。

![form2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173323/20d4830a-2a36-0b7f-d5fa-fac396e6c974.gif)


```jsx:Form2
import React from 'react';
import { useForm, Controller, ErrorMessage } from 'react-hook-form';
import { TextField, Button } from "@material-ui/core";

export default function Form2() {
  const { handleSubmit, errors, control } = useForm()
  const onSubmit = (data: Object) => { console.table(data) };

  return (
    <div className="form form2">
      <h1>Form2</h1>
      <form onSubmit={handleSubmit(onSubmit)}>
        <Controller
          as={<TextField />}
          name="form2_1"
          control={control}
          rules={{ required: "必須です" }}
          defaultValue=""
        />
        <div className="errors">
          <ErrorMessage errors={errors} name="form2_1" />
        </div>
        <Controller
          as={<Button color="primary" ><span>送信</span></Button>}
          name="submit"
          control={control}
          defaultValue=""
          onClick={handleSubmit(onSubmit)}
        />
      </form>
    </div>
  )
}

```

## reset
フォーム内のvaluesとerrorsをリセットできる関数。
リセット時に値を渡すとデフォルトの値としてリセットできる。
![form3.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173323/c99845f4-4447-d431-416f-8882c5720892.gif)

```jsx:Form3
import React, { useCallback } from 'react';
import { useForm, ErrorMessage } from 'react-hook-form'

type Reset = (values?: Record<string, any>) => void;

export default function Form3() {
  const { register, handleSubmit, reset, errors }: ({
    register: Function,
    handleSubmit: Function,
    reset: Reset,
    errors: any,
  }) = useForm();
  const onSubmit = (data: Object) => { console.table(data) };
  const onReset = useCallback(() => reset(), [reset])
  const onDefaultReset = useCallback(() => reset({ first_name: 'ジョン', last_name: '万次郎' }), [reset])

  return (
    <div className="form form3">
      <h1>Form3</h1>
      <form onSubmit={handleSubmit(onSubmit)}>
        <span>姓:</span>
        <input
          name="last_name"
          ref={register({ required: '姓は必須です。' })}
        />
        <div className="errors">
          <ErrorMessage errors={errors} name="last_name" />
        </div>
        <span>名:</span>
        <input
          name="first_name"
          ref={register({ required: '名は必須です。' })}
        />
        <div className="errors">
          <ErrorMessage errors={errors} name="first_name" />
        </div>
        <input type="submit" />
        <input
          type="button"
          onClick={onReset}
          value="reset"
        />
        <input
          type="button"
          onClick={onDefaultReset}
          value="reset + default set"
        />
      </form>
    </div>
  )
};

```



## setError / clearError
inputのエラーを手動で設定したりクリアする。

## setValue
値を動的に設定できる。

![form4.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173323/c78204e7-4cdd-d909-28bc-ba980dc8fd9f.gif)


```tsx:Form4
import React, { useCallback } from 'react';
import { useForm, ErrorMessage } from 'react-hook-form'

export default function Form4() {
  const { register, handleSubmit, setError, clearError, errors, setValue } = useForm();
  const onSubmit = (data: any) => {
    const goodAnswer = 12 * 12;
    if (parseInt(data.answer, 10) !== goodAnswer) return setError('answer', 'notMatch', '不正解です');
    clearError('answer');

    console.log('正解');
  };
  const onSetValue = useCallback(() => {
    setValue('answer', 12 * 12);
  }, [setValue]);

  return (
    <div className="form form4">
      <h1>Form4</h1>
      <form onSubmit={handleSubmit(onSubmit)}>
        <span>12 × 12 = </span>
        <input
          name="answer"
          ref={register({ required: '入力してください' })}
        />
        <div className="errors">
          <ErrorMessage errors={errors} name="answer" />
        </div>
        <input
          type="submit"
          value="回答"
        />
        <input
          type="button"
          value="諦める"
          onClick={onSetValue}
        />
      </form>
    </div>
  )
};

```

## getValues
フォーム全体のデータを返す。

```react
<input name="test" ref={register} />
<input name="test1" ref={register} />
<button
  type="button"
  onClick={() => {
    const values = getValues()
    console.log(values) // e.g: { test: [1, 2], test1: { data: '23' } 
  }}
>
  GetValues
</button>
```

## triggerValidation
バリデーションのトリガーを手動で設定できる。

```jsx
<input name="lastName" ref={register({ required: true })} />
  <button
    type="button"
    onClick={async () => {
      triggerValidation("lastName");
    }}
  >
  Trigger
</button>
```

## unregister
inputにunregisterを適用すると、フォームデータに含まれなくなる。
> これは、 useEffect でカスタム登録として input を登録 (register) し、 コンポーネントのアンマウント後に登録を解除する場合に便利です。

らしい。


## formState
フォームの状態がオブジェクトで入っている。

|name|description|
|:--|:--|
|dirty|入力が行われた後trueになる|
|isSubmitted|submitされた後trueになる|
|isSubmitting|送信中はtrue, 送信後falseになる|
|touched|操作されたnameが配列で入る|
|submitCount|submitされた回数|
|isValid|errorがない場合true|

おわりです。込み入ったことは試していないのですが、記述が簡単な印象でした。