react-hook-form超入門 【React】サンプル付き

◆ Live配信スケジュール ◆
サイオステクノロジーでは、Microsoft MVPの武井による「わかりみの深いシリーズ」など、定期的なLive配信を行っています。
⇒ 詳細スケジュールはこちらから
⇒ 見逃してしまった方はYoutubeチャンネルをご覧ください
【4/18開催】VSCode Dev Containersで楽々開発環境構築祭り〜Python/Reactなどなど〜
Visual Studio Codeの拡張機能であるDev Containersを使ってReactとかPythonとかSpring Bootとかの開発環境をラクチンで構築する方法を紹介するイベントです。
https://tech-lab.connpass.com/event/311864/

今回はReactで簡単にフォームを扱うために【react-hook-form】の使用方法について紹介していきます。超入門です。【react-hook-form】でバリデーションの追加やバリデーションを監視してコンポーネントを変更する挙動についても紹介していきます。

初めに

どもども!最近はReactの記事を増やしていこうと考えている龍ちゃんです。弊社のチームでフロントを専門に扱っている人が皆無という現状で戦っているのですが、時には心が疲弊してしまいます。そんな時は、猫と犬の動画を見て心を落ち着かせています。昔は温泉に行く頻度よりも猫カフェに行く頻度が高かったのですが、埼玉に越してからは温泉の方がよくいきますね。

さて、本題に戻りましょう。今回は、Reactで簡単にFormの管理が可能になる【react-hook-form】について紹介していきます。Formを使用する際は、とりあえず導入しておけばレンダリング数の防止にもなりますし、バリデーション管理もすごくやりやすいです。このブログを読んだらわかるようになることの予定は以下です。

  • react-hook-formの利点について
  • 【react-hook-form】基本的な使い方
    • フォームの作成
    • バリデーション
    • バリデーションエラーの表示

とりあえず、基本的な部分を抑えていきたいと思います。

react-hook-formって何?

ここでは【react-hook-form】について理解を深めていきたいと思います。とりあえずおしゃれな公式サイトがあるので覗きに行きましょう。公式サイトに一通り利点について言及があります。残念なことに日本語対応はされていないので翻訳アプリを噛まして読みましょう。ざっくりと以下に利点を置いておきます。

  • 軽量で高速(フォームの管理のみに注力している・HTMLをベースに構成)
  • シンプルなAPI(Reactのフックで処理できる)
  • 柔軟性がある
  • 性能が良い(再レンダリングが最小限)
  • 簡単なバリデーション(バリデーションルールを設定するだけ)
  • 容易なテスト(テスト用APIによって簡単に行える)

公式サイトにはほかにもいろいろと利点が書いてあります。React Nativeでも使用することができるようです。有志による開発で、Discordで議論が行われているようです。ちなみにTwitterもあります。開発コミュニティがTwitterを運営していると情報がリアルタイムにキャッチできるのでお勧めです。

基本的な使い方

ここでは基本的な使い方について説明をしていきます。ライブラリとインストールと単純なバリデーションとエラーメッセージを表示するサンプルです。

セットアップ

ライブラリなのでインストールするだけで使用することができます。

yarn add react-hook-form

以上でライブラリのインストールは完了です。

基本的な使い方

基本的な使い方をまとめたコンポーネントを作成しました。挙動のイメージは以下になります。

react-hook-formサンプルの挙動Gif

 

こちらのサンプルでは、以下の機能を持っています。

  • 初期値の登録
  • バリデーション
    • required
    • validate
  • エラーメッセージの表示
  • 値の監視
  • 挙動確認のためエラー状態・バリデーションの状態の監視
  • バリデーションによってのデザイン切り替え

それではコードです。

import { useEffect } from 'react';
import { useForm, SubmitHandler } from 'react-hook-form';

export const HookFormTest0 = () => {
  type InputType = {
    id: string;
    password: string;
    password2: string;
  };
  const {
    register,
    handleSubmit,
    watch,
    formState: { errors, isValid },
  } = useForm<InputType>({
    defaultValues: {
      id: '',
      password: '',
      password2: '',
    },
  });

  const onSubmit: SubmitHandler<InputType> = (data: InputType) => {
    console.log('data', data);
  };
  useEffect(() => {
    console.log(isValid, errors);
  }, [errors, isValid]);

  return (
    <>
      <form onSubmit={handleSubmit(onSubmit)} className="flex w-96 flex-col items-center gap-10">
        <label className="flex w-full flex-row items-center justify-between gap-3">
          <span>ID:</span>
          <input
            type="text"
            maxLength={16}
            {...register('id', {
              required: { value: true, message: 'name is required' },
              maxLength: { value: 15, message: 'name value maxLength 15' },
            })}
          />
        </label>
        <div>{errors.id && errors.id.message}</div>
        <label className="flex w-full flex-row items-center justify-between gap-3">
          <span>PASSWORD:</span>
          <input
            type="password"
            {...register('password', {
              required: { value: true, message: 'password is required' },
              validate: {
                password: (v: string) =>
                  v.includes('password') ? 'passwordは安直すぎませんか?' : undefined,
                number: (v: string) => (v.includes('123') ? '123は安直すぎませんか?' : undefined),
              },
            })}
          />
        </label>
        <div>{errors.password && errors.password.message}</div>
        <label className="flex w-full flex-row items-center justify-between gap-3">
          <span>PASSWORD2:</span>
          <input
            type="password"
            {...register('password2', {
              required: { value: true, message: 'password is required' },
              validate: {
                message: (v: string) =>
                  v !== watch('password') ? 'password not match password' : undefined,
              },
            })}
          />
        </label>
        <div>{errors.password2 && errors.password2.message}</div>
        <input
          type="submit"
          value={isValid ? '送信する' : '不許可'}
          className={isValid ? 'border-black text-black' : 'border-red-500 text-red-500'}
        />
      </form>
    </>
  );
};

基本の使い方としては、公式掲載のサンプルと近いものがあります。バリデーションルールとしては以下のパラメータがあります。

  • required
  • min
  • max
  • minLength
  • maxLength
  • pattern
  • validate

「pattern」は正規表現を使用してValidationを出すことができます。「validate」では関数を自前で実装することでバリデーションを実装することができます。「validate」以外はオブジェクトの形で以下の形式で指定することができます。

プロパティ名: { value: プロパティによる, message: 'password is required' },

「validate」の場合は独自にプロパティを設定するイメージで条件を指定していきます。

validate: {
  password: (v: string) =>
    v.includes('password') ? 'passwordは安直すぎませんか?' : undefined,
  number: (v: string) => (v.includes('123') ? '123は安直すぎませんか?' : undefined),
},

undefinedが返答された場合は、バリデーション通過と認識されます。Stringの配列を返答することで複数の自作バリデーションを作成することができます。

バリデーションに通過しなかった場合はerrorsの中にオブジェクト形式でメッセージが格納されます。またバリデーションエラーの場合ではisValidにFALSEが格納されています。通過した場合には即時にTRUEに代わります。こちらを使用することで、デザインを変更することでユーザー側に通知することができます。サンプルでは挙動を確認するためにerrorsisValidの値をコンソールに出力しています。

バリデーションルールにしれっと出ていますが、watchを使用することで値を取得することができます。今回は、passwordpassword2を比較して一致した場合にバリデーションを通過するように作成しています。

終わりに

お疲れ様です。今回は【react-hook-form】の基礎的なサンプルを用意しました。内容としては、公式リファレンスの内容を一個にギュッとした感じになっています。一個サンプルを持っておくことで、別プロジェクトで使用する際に参照元ができて楽です。皆さんも元となるコードをストックしておきましょう。ブックマークが大量になってしまうのが悩みですね。

次回は、【react-hook-form】を自作コンポーネントで使用する方法や動的にフォームを増やすことができるuseFieldArrayの使用方法についての解説を入れていきたいと思います。

今回のサンプルにも使用しているTailwindの記事を置いておきます。

それではまた~

アバター画像
About 龍:Ryu 107 Articles
2022年入社で主にフロントエンドの業務でTailwindと遊ぶ日々。お酒とうまいご飯が好きで、運動がちょっと嫌いなエンジニアです。しゃべれるエンジニアを目指しておしゃべりとブログ執筆に注力中(業務もね)//
ご覧いただきありがとうございます! この投稿はお役に立ちましたか?

役に立った 役に立たなかった

0人がこの投稿は役に立ったと言っています。


ご覧いただきありがとうございます。
ブログの最新情報はSNSでも発信しております。
ぜひTwitterのフォロー&Facebookページにいいねをお願い致します!



>> 雑誌等の執筆依頼を受付しております。
   ご希望の方はお気軽にお問い合わせください!

Be the first to comment

Leave a Reply

Your email address will not be published.


*


質問はこちら 閉じる