🦔

打倒React Hook Formを掲げてもくもく会を主催したが、結局Rhf強ぇ〜ってなった話

2024/02/01に公開

React Hook Form、便利ですよね。
とはいえReactのFormライブラリ自体は複数あるはずで、今はReact Hook Formが人気だけどそのうち覇権が移ることもあるのではと思い、以下のようなツイートをしてみたところ、思いのほか反応がありました。

https://twitter.com/Meijin_garden/status/1742738322198569302

という流れで以前から仲良くさせていただいているアセンド株式会社の方にお声がけいただいて、あれよあれよというまにconnpassが立ち上がりました。

https://ascend.connpass.com/event/306913/

この記事は、打倒React Hook Formを掲げて冬の夜にもくもく会に集まった約10人の猛者たちが、結局React Hook Form強ぇ〜ってなるまでのお話です。

ライブラリのリストアップ

もくもく会自体はゆるふわで、唯一の参加条件は自ら手を動かして学ぼうとする者、といった感じで行いました。

参加者がリストアップした、React Hook Formを打倒してくれそうなライブラリは以下のとおりです。これらのライブラリを各自分担して試していくような段取りで進みました。
一応前提として、Next.jsを使っている想定です。

  • houseform
  • @tanstack/form
  • uniform
  • mobx-react-form
  • react-final-form

もっとも、参加者のモチベーション自体はバラバラで、これを機にRhf自体の理解度も高めたいなという方もいれば、私は社内でRhfの内製ラッパーを開発しているので(参考記事)、同等以上の仕組みを構築できるライブラリ、かつ業務利用する判断も下せる将来性のあるライブラリを探していました。

意外とメンテナンスされているライブラリ少ない説、浮上

もくもく会としてスタートした本イベントですが、特に私個人としては業務利用も想定していたので、まずはメンテナンスの頻度や現バージョンを見ていこうというところで調べていきました。

※開催時点

まだまだ候補は複数あると思いますが、ざっと5つ調べたところ活発にメンテナンスされているといえるのがtanstackとmobxのみとなります。final formは本当にfinalを迎えている説が濃厚でした。

mobxを調べていただいている参加者の方もいたのですが、結論としてはそもそもMobXに詳しくないと思想が理解できないなという見解になりました。
ということで、メンテナンス性だけ見れば意外とtanstack一強に近い状態なのでは、と思い、以後もくもく会の時間を私自身はtanstackによるラッパー実装に使っていきました。

激震!Sunset事件

ところで、メンテナンス性の評価をするにしては最終更新だけ見ているのもどうかと思い、もくもく会の趣旨をガン無視することにはなりますがメンテナーの状況をネトストしにいくことにしました。

houseformのメンテナーについて、以下の手順でTwitterを特定しました。

すると、この方、プロフィールに「tanstack maintainer」と書いているじゃありませんか。

そこでtanstack formのVersionsを再び見ると、初期開発は9ヶ月ほど前ですが、開発が加速したのは5ヶ月前となります。
houseformのメンテナンスが止まったのも、5ヶ月前。

つまり、houseformのメンテナーがtanstackに異動する形でフォームの開発を継続しているのでは?という推論が成り立ちます。

さらにネトストを続けていると、決定的な発言がありました。

https://twitter.com/crutchcorn/status/1740246046041940114

要は「Houseformは今のところ安定しているAPIなので、今この瞬間はTanStackより推奨だよ。でも、Tanstack Formがv1.0になったらSunsetだよ」

「it will be sunset」

見慣れない表現ですが、これは要は日没→メンテ終了、という意味でしょう。

(↓っていう発言をXでしたらCorrect✅とリプもらった図)

https://x.com/tannerlinsley/status/1749844432931967261?s=20

Codemodの提供は検討しているとのことだし、ドキュメントを見た感じField Firstな思想は引き継いでいそうとはいえ、今からhouseformを使い始めるくらいなら0系だけどTanstackを使ったほうがよさそうです。

もくもく会メンバーに共有したところ、It will be sunsetという表現で大いに盛り上がりました。
同様に、2024年になってもメンテナンスを続けているTanstackやMobXに対して、「これはSunrizeだ!(訳:年越しして初日の出を迎えている)」と発言したりしました(?)

Tanstackの感想

Tanstack Formをいじってみた個人的な感想なのですが、まずuseFormにonSubmitの関数を渡すインターフェースはそちらのほうがわかりやすいので好みでした。RhfはhandleSubmitがフックから返ってくるのでそれでSubmit時の処理をラップする思想ですので、メリデメあるかもですが僕はTanstackのほうが好きですね。フック使った時点で準備万端って感じなので。

houseformから引き継がれているField Firstの思想ですが、これが大きくてReact Hook Formからそのまま移行するというわけにはいかなさそうです。

以下のように、各フィールドのほうにバリデーションルールを書きます。これにより、useForm実行時点でバリデーションスキーマを確定させなくていいので柔軟なフォーム回答項目の追加削除ができるかもしれません。

          <div>
            {/* A type-safe field component*/}
            <form.Field
              name="firstName"
              validators={{
                onChange: z
                  .string()
                  .min(3, 'First name must be at least 3 characters'),
                onChangeAsyncDebounceMs: 500,
                onChangeAsync: z.string().refine(
                  async (value) => {
                    await new Promise((resolve) => setTimeout(resolve, 1000))
                    return !value.includes('error')
                  },
                  {
                    message: "No 'error' allowed in first name",
                  },
                ),
              }}
              children={(field) => {
                // Avoid hasty abstractions. Render props are great!
                return (
                  <>
                    <label htmlFor={field.name}>First Name:</label>
                    <input
                      id={field.name}
                      name={field.name}
                      value={field.state.value}
                      onBlur={field.handleBlur}
                      onChange={(e) => field.handleChange(e.target.value)}
                    />
                    <FieldInfo field={field} />
                  </>
                )
              }}
            />
          </div>

もくもく会当時はあまりメリットを感じなかったのですが、今思うと、各フィールドのコンポーネントにバリデーションルールを指定できるというのは、フォームの各項目が動的に増えたり減ったりする場面においてとても使い勝手がよくなる気がしました。ちょっと複雑なフォームだとすぐにそういう要件が来ますからね。SubmitボタンのDisabledは要は個々のバリデーションルールの中で1つでも違反していたらONにするというだけであって、あらかじめuseForm実行時点でスキーマを固定しなければいけないということとイコールでは有りません。

率直な感想として、フィールドごとにバリデーション指定できたほうがよくない?みたいな意見を思い浮かぶこともさることながら、その概念に「Field First」って名前をつけるのもマーケティング的に上手だなと思いました。概念への名付けって上手い下手がすごく出そうなので、そういう上手さみたいなところは見習いたいです。

React Hook Formを養護するに足る見解

前節では、Tanstackを褒めてきましたが、まずもってドキュメントが現在全然足りていなくて、実コードを読みに行くしか無いです(0系なので当たり前)。
React Hook Formに対する不満として、冒頭で参加者から上がったのが「事あるごとにドキュメントを見に行かないといけない。同じことを実現する書き方が多すぎる」といった意見だったのですが、これは裏返すとあらゆる機能に対してドキュメントが用意されているという信頼があるともとれます。その点において、ドキュメンテーションを積み上げてきたReact Hook Formの信頼度は高いです。

また、参加者の1人がめちゃめちゃ手厚く検証いただいていたのですが、Tanstackはネストしたフォームにおける型エラーが目立つようで、そのへんは型定義を鬼のように組み上げていることでお馴染みのReact Hook Formに軍配が上がっています。

ちなみに、uniformを始めとするいくつかのフォームライブラリは全く思想が異なっており、JSON Schemaといったあらかじめ決められた形式のオブジェクトを外から渡すと、その仕様に沿ったフォーム部品がレンダリングされるというものでした。

これ自体は僕も3年くらい前にサーバーサイドエンジニアでもVueでFormを作れるようにするために、JSON渡すだけでFormできる君みたいな神コンポーネント(皮肉)を作成して、見事メンテナンスが破綻した経験があるので、一定の状況下でとてもワークする選択肢だと思うとともに、カスタマイズ可能な思想で展開しているRhfの現実的な競合がTanstackしか見当たらなかったというところで、やっぱりRhf強いなぁと感じました。

まとめ

仕事でこれでデファクトだろという選択に甘んじていることは往々にしてあると思いますが、それを突き破って今後新興勢力になるであろう技術をキャッチアップするのは、各自専門領域もあると思うので必ずしもできることではありません。今回のようにもくもく会を企画して短時間で一気に一定の結論を獲得しに行くのはとても有意義だなと思いました。会場を貸していただいたアセンドさん、参加いただいた方々、ありがとうございます!

今回のもくもく会で使ったTemplate Repoです。

https://github.com/m-moeka/react-form-library-comparison-template

僕が多少書き足したあとのコードはこちら。

https://github.com/TeXmeijin/react-form-library-comparison-template-copied/tree/main

マナリンク Tech Blog

Discussion