バリデーションにreact-hook-formが便利だった


はじめに

react-hook-formを使ってみて、とても便利で「もうこれなしでは生きていけないぜ。。」という感じだったので布教するためにこの記事を書きました。
サンプルの動くコードもあるので、以下のリンクを見ながら読み進めてもらえると比較しやすいと思います!

サンプル: https://282haniwa.github.io/react-hook-form-example/
リポジトリ: https://github.com/282Haniwa/react-hook-form-example/

react-hook-formとは

Reactでのformの入力を簡単に管理して検証するために開発されたライブラリです。
React 16.8で追加されたHookを使ってformの管理・検証を簡単にできるようにすることを目的に作られました。

react-hook-formを使うことで、formの値を管理するstateの宣言や、onChangeを使ったinputの値の変更のハンドリングが必要なくなるので簡単にformを扱うことができます。

また、バリデーションに関しても基本的なものはデフォルトで実装されている上に、バリデーションの実行されるタイミングもreact-hook-formで管理されているためonChangeonSubmitでバリデーションを実行するようにロジックを書く必要もなくなります。

サンプルコード

react-hook-formでは、名前と年齢を入力するフォームを以下のように実装することができます。

import React from 'react'
import { useForm } from 'react-hook-form'
import styles from 'src/styles/components/Example2.module.css'

const numberRegExp = /^[0-9]+$/

type FormState = {
  firstName: string
  lastName: string
  age: number
}

type Props = {}

export const Example2WithRHF: React.FC<Props> = () => {
  const { register, handleSubmit, reset, errors } = useForm<FormState>({
    mode: 'onChange',
    defaultValues: {
      firstName: '',
      lastName: '',
      age: 18,
    },
  })

  const onSubmit = handleSubmit((data) => {
    console.log(data)
  })

  return (
    <div>
      <form onSubmit={onSubmit}>
        <div className="flex-column">
          <span>first name</span>
          <input
            name="firstName"
            type="text"
            ref={register({
              required: '必須項目です',
              maxLength: {
                value: 20,
                message: '20文字以内で入力してください',
              },
            })}
          />
          <span className={styles['error-message']}>
            {errors.firstName?.message}
          </span>
        </div>
        <div className="flex-column">
          <span>last name</span>
          <input
            name="lastName"
            type="text"
            ref={register({
              required: '必須項目です',
              maxLength: {
                value: 20,
                message: '20文字以内で入力してください',
              },
            })}
          />
          <span className={styles['error-message']}>
            {errors.lastName?.message}
          </span>
        </div>
        <div className="flex-column">
          <span>age</span>
          <input
            name="age"
            type="number"
            ref={register({
              valueAsNumber: true,
              required: '必須項目です',
              pattern: {
                value: numberRegExp,
                message: '整数で入力してください',
              },
              min: {
                value: 18,
                message: '18以上の数字を入力してください',
              },
              max: {
                value: 100,
                message: '100以下の数字を入力してください',
              },
            })}
          />
          <span className={styles['error-message']}>{errors.age?.message}</span>
        </div>
        <button type="button" onClick={() => reset()}>
          リセット
        </button>
        <button>送信</button>
      </form>
    </div>
  )
}

inputのrefにregisterを渡すことで、そのinputの値をハンドリングすることができます。
registerの引数にはバリデーションのルールを渡したり、その値を数字として扱うことを設定できます。

実際にどういうことができるのかはドキュメントを読んでみてください。

上のコードを見てもらえればわかるように、formの値をstateで管理したりしていません。
本来であれば、

// ...
const [firstName, setFirstName] = useState('')
const [firstNameError, setFirstNameError] = useState('')
const onChangeFirstName = (event) => {
  setFirstName(event.target.value)
  setFirstNameError(validateFirstName(event.target.value))
}
// ...
<input onChange={onChangeFirstName} value={firstName} />

のようにして、inputの入力をonChangeでstateにセットしたりしないといけないですが、react-hook-formが内部でうまくしてくれているおかげで上のような実装をしなくてすみます。

また、react-hook-formでは入力値をstateで管理するような実装になっていないので、inputに入力するたびにstateが変化して再レンダリングされるというようなことが起きないので、パフォーマンスの改善にもつながります。

こちらreact-hook-formを使わない実装と比較したコードが実際に動いているので、是非見てreact-hook-formの素晴らしさを実感してください!

まとめ

バリデーションを簡単にできるライブラリがないかなーと思って探していたところ、react-hook-formを見つけて、とても便利だったので記事にしてみました。
コードの記述量が減って見通しが良くなるので個人的にはとても気に入っています。

是非みなさんも使ってみてください!

最後まで読んでいただきありがとうございました。
他に「こんなライブラリも便利だよ!」みたいなのがあれば、是非コメント欄で教えてください!

参考