LoginSignup
64
43

More than 3 years have passed since last update.

とりあえずreact-hook-form

Last updated at Posted at 2020-01-25

はじめに

react系のフォームバリデーションライブラリreact-hook-formのざっくり使い方です。

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

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

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

formikとの比較

download

スクリーンショット 2020-01-16 0.49.24.png
https://www.npmtrends.com/redux-form-vs-formik-vs-react-hook-form

size

formik

スクリーンショット 2020-01-16 0.54.11.png

react-hook-form

スクリーンショット 2020-01-16 0.54.30.png

API

useForm

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

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に登録する

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

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

<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コンポーネントも用意されている。

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 で指定された値を返します。

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

handleSubmit

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

// ~~
const onSubmit = (data: Object) => { console.table(data) };
// ~~
<input type="submit" />
// ~~
async関数も渡すことができる
handleSubmit(async (data) => await fetchAPI(data))

form1.gif

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

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

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

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

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

<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

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

<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

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

64
43
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
64
43