EY-Office ブログ

React Hook Form等は本当に必要なのでしょうか?

久しぶりに入力欄がたくさん並んでいる登録フォームをReactで作る事にになりました。
少し前であれば、こういう場合はReact Hook Formformikreact-final-formなどを使っていました。しかし今回の案件で使うべき理由が見当たりませんでした。

Astro AIいらすとやが生成した画像です

Reactで作るFormを比較してみた

ReactのみでFormを作る場合には2つの方法があるので、これらとReact Hook Formを比較してみましょう。

Controlled: ステートを使う

入力された文字や選択された状態はステート(State)なので、useState等でステートを作り、キー入力毎にステートを更新する方法です。

  • ① useStateでステートを作成
  • ② ステートの現在の値はuseState()の最初の戻り値で参照できます
  • ③ キー入力毎にuseState()の2番目の戻り値のset〜()関数でステートを更新します
import { useState } from 'react'

function App() {
  const [text1, setText1] = useState("");  // ← ①
  const [text2, setText2] = useState("");
  const [text3, setText3] = useState("");
  const [text4, setText4] = useState("");
  const [text5, setText5] = useState("");

  const onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    console.log("text1:", text1);          // ← ②
    console.log("text2:", text2);
    console.log("text3:", text3);
    console.log("text4:", text4);
    console.log("text5:", text5);
  }

  return (
    <>
      <form onSubmit={onSubmit}>
       <div>
          <input type="text" placeholder="テキスト1"
            value={text1} onChange={e => setText1(e.target.value)} />  {/* ← ③ */}
        </div>
        <div>
          <input type="text" placeholder="テキスト2"
            value={text2} onChange={e => setText2(e.target.value)} />
        </div>
        <div>
          <input type="text" placeholder="テキスト3"
            value={text3} onChange={e => setText3(e.target.value)} />
        </div>
        <div>
          <input type="text" placeholder="テキスト4"
            value={text4} onChange={e => setText4(e.target.value)} />
        </div>
        <div>
          <input type="text" placeholder="テキスト5"
            value={text5} onChange={e => setText5(e.target.value)} />
        </div>
        <div>
          <button>Submit</button>
        </div>
      </form>
    </>
  )
}

Uncontrolled: DOMから直接値を取得

もう1つはDOM(ブラウザーの画面に表示されている要素)から現在の値を取得する方法で、このためにはuseRefを使います。

  • ① useRefでDOM参照を準備(実はuseRefは、それ以外にも使い道がありますが・・・)
  • .currentでDOM要素を参照できます。ただし初期値はnullなので?.でvalueを参照します
  • ③ 画面描画時にref=に指定されたDOM参照が設定されます
import { useRef } from 'react'

function App() {
  const text1 = useRef<HTMLInputElement>(null);   // ← ①
  const text2 = useRef<HTMLInputElement>(null);
  const text3 = useRef<HTMLInputElement>(null);
  const text4 = useRef<HTMLInputElement>(null);
  const text5 = useRef<HTMLInputElement>(null);

  const onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    console.log("text1:", text1.current?.value);  // ← ②
    console.log("text2:", text2.current?.value);
    console.log("text3:", text3.current?.value);
    console.log("text4:", text4.current?.value);
    console.log("text5:", text5.current?.value);
  }

  return (
    <>
      <form onSubmit={onSubmit}>
        <div>
          <input type="text" placeholder="テキスト1" ref={text1} />  {/* ← ③ */}
        </div>
        <div>
          <input type="text" placeholder="テキスト2" ref={text2} />
        </div>
        <div>
          <input type="text" placeholder="テキスト3" ref={text3} />
        </div>
        <div>
          <input type="text" placeholder="テキスト4" ref={text4} />
        </div>
        <div>
          <input type="text" placeholder="テキスト5" ref={text5} />
        </div>
        <div>
          <button>Submit</button>
        </div>
      </form>
    </>
  )
}

React Hook Form

React Hook Formを使うとコンパクト(?)にFormを構築できます。

  • ① フォームで入力される値の型定義
  • ② useForm()から登録関数やsubmitハンドラーを取得
  • ③ 入力値は①で定義した型のオブジェクトになっています
  • ④ 入力要素の登録
import { useForm, SubmitHandler } from "react-hook-form";

type FormValues = {         // ← ①
  text1: string;
  text2: string;
  text3: string;
  text4: string;
  text5: string;
};

function App() {
  const { register, handleSubmit } = useForm<FormValues>();  // ← ②

  const onSubmit: SubmitHandler<FormValues> = (data) => {
    console.log("text1:", data.text1);                       // ← ③
    console.log("text2:", data.text2);
    console.log("text3:", data.text3);
    console.log("text4:", data.text4);
    console.log("text5:", data.text5);
  };

  return (
    <>
      <form onSubmit={handleSubmit(onSubmit)}>
        <div>                                          {/* ↓ ④ */}
          <input type="text" placeholder="テキスト1" {...register("text1")} />
        </div>
        <div>
          <input type="text" placeholder="テキスト2" {...register("text2")} />
        </div>
        <div>
          <input type="text" placeholder="テキスト3" {...register("text3")} />
        </div>
        <div>
          <input type="text" placeholder="テキスト4" {...register("text4")} />
        </div>
        <div>
          <input type="text" placeholder="テキスト5" {...register("text5")} />
        </div>
        <div>
          <button>Submit</button>
        </div>
      </form>
    </>
  )
}

メリット: なぜReact Hook Formは使われているのか?

上の3つを眺めてみると、行数は同じくらいです。強いて言えばステートを使うはinputタグはonChangeハンドラーのぶん長くなっています。
それではなぜ、人類はReact Hook Formを使うのでしょうか?

React Hook Formのホームページには Performant, flexible and extensible forms with easy-to-use validation. と目立つ文字色で書かれていますが、重要なのは easy-to-use validationではないかと思います。入力間違いのチェックが手軽に、しかもエラーメッセージの表示等形式等もカスタマイズできます。

歴史的には、React Hook Form v1.0.0がリリースされたのは2019年3月で、ReactはまだHooksをサポートしてないクラス・コンポーネントの時代で、ステートを使うとコードがやたらと長くなってしまう時代ですた。 さらにバージョン6まではIE11もサポートしていました。😅

あとは、日本語情報が豊富と言うかネット上には「React Hook Formを使えばFormが超簡単に作れる〜」のような記事があふれています。

デメリット: React Hook Formは使いやすいですか?

以前 Autocompleteの初期値が設定されないです? というブログを書きましたが、ここでサポートしていた企業ではReact Hook FormとMUI(Material UI)を組み合わせて使っていました。
React Hook FormでMUIを使うにはIntegrating with UI librariesなるように直接MUIは使えずControllerコンポーネントを使う必要があり、コードが複雑になり可読性が下がってしまいます。

また、非互換のバージョンアップが起きることがあります Migration Guide: V6 to V7 をみるとV6からV7にアップデートするにはコードを書き換えないといけない事がわかります。

さらにIE11無き今、ValidationはHTML5のValidationが使えます、以前 HTML5 Form Validationのことを思い出したのでMUIで使ってみたブログも書きましたが、複雑なエラーチェックやエラー表示形式に拘らなければ、HTML5 Validationは結構簡単に書けます。

まとめ

React Hook Formは、React V16.7以前、そしてIE11が存在した時代には、とても有用なライブラリーだったと思います。
しかしReactが進歩し、IE11が無くなった現在に、無自覚にこのような便利系ライブラリーを使うかどうかは、メリット・デメリットをよく検討してから決めるべきだと思います。

- about -

EY-Office代表取締役
・プログラマー
吉田裕美の
開発者向けブログ