はじめに

みなさん、フォームライブラリは何を使っていますか?

React Hook Formがフォームの一時代を築いた後に、server actionに対応しているConformが登場したり、様々なフォームライブラリがあると思いますが、今回は最近v1になったTanStack Formについてご紹介します。

TanStack Formは、各入力フィールドを独立したコンポーネントとして提供します。これにより、フォーム全体を再描画するのではなく、変更が生じた特定のフィールドのみを選択的に再レンダリングすることが可能となり、パフォーマンスの最適化を実現しています。

https://tanstack.com/form/latest

https://github.com/tanstack/form

TanStack Formの推しポイント👍

まずは、TanStack Formの推しポイントをいくつかご紹介します。

強力な型サポート

TanStack系のライブラリ全般の特徴になりますが、TypeScriptでできているため型安全・型補完が効くため開発体験がとても良いです。

様々なスキーマライブラリに対応

以下の様々なスキーマライブラリに対応しているのも魅力の一つです。複数のスキーマライブラリ候補から選択できることは嬉しいポイントですね!(最近、ArkTypeも流行っていますし…!!)

非同期バリデーション

これが私が一番感動した激推しポイントになります。以下のように実装するだけで非同期バリデーションが可能になるんです!!

form.Field
  name="age"
  asyncDebounceMs={500}
  validators={{
    onBlurAsync: async ({ value, signal }) => {
      const currentAge = await fetchCurrentAgeOnProfile({ signal })
      return value  currentAge ? 'You can only increase the age' : undefined
    },
  }}
/>

React Hook Formなどでは非同期バリデーションを行うために、debounceを一から実装する必要がありました。TanStack Formではバリデーションのタイミングをコントロールしやすいように、asyncDebounceMsなどのプロパティが存在し、わざわざdebounceを実装しなくても大丈夫なのです!!

https://tanstack.com/form/latest/docs/framework/react/guides/validation#built-in-debouncing

様々なフレームワーク/ライブラリをサポート

TanStack Formは、様々なフレームワーク/ライブラリで動作するように別々のパッケージが提供されています。各フレームワーク/ライブラリ間で一部機能が異なりますが、コアのAPIは共通であり、フレームワーク/ライブラリに影響されずにTanStack Formを選定することができます。

サポートされているフレームワークは以下の通りです。(ReactNativeにも対応)

Next.jsのserver actionに対応

現在、Next.jsのserver actionに対応している主要なフォームライブラリはConformであり、server actionを使用する場合はConformの一択かなと考えていました。しかし、TanStack FormもNext.jsのserver actionに対応しており、有力な選択肢として上がってくるのではないでしょうか。

https://tanstack.com/form/latest/docs/framework/react/examples/next-server-actions

React Hook Formとの違い

状態管理の違い

React Hook FormはReactのStateでデータを持っているのに対し、TanStack FormはObserverパターンでDOMで値を管理します。そのため、React以外のライブラリでも使用することができるようになっているようです。

インストールサイズが大きい

残念ながら、TanStack FormはReact Hook Formと比べてインストールサイズは大きいです。これはTanStack系のライブラリは強力な型サポートがある分、それがインストールサイズに直結しているようです。

https://packagephobia.com/result?p=@tanstack/react-form

https://packagephobia.com/result?p=react-hook-form

ただ、バンドルサイズとしては問題がないようです。

他フォームライブラリの比較についても公式がまとめているので、気になれば見てみてください!

https://tanstack.com/form/latest/docs/comparison

実装編

TanStack Formを使って簡単なログインフォームを作成しました。コードの全体は以下です。

import { useForm } from "@tanstack/react-form";
import React from "react";
import { loginSchema } from "./schema/login";

export const Form: React.FC = () => {
  const form = useForm({
    defaultValues: {
      email: "",
      password: "",
    },
    onSubmit: ({ value }) => {
      console.log(value);
    },
    validators: {
      onChangeAsync: loginSchema,
      onChangeAsyncDebounceMs: 500,
    },
  });

  const handleSubmit = (e: React.FormEventHTMLFormElement>) => {
    e.preventDefault();
    form.handleSubmit();
  };

  return (
    form onSubmit={handleSubmit}>
      {(field) => (
          div>
            label htmlFor={field.name}>Emaillabel>
            input
              type="email"
              id={field.name}
              name={field.name}
              value={field.state.value}
              onChange={(e) => field.handleChange(e.target.value)}
            />
            {field.state.meta.isTouched &&
              field.state.meta.errors.map((error) => {
                return p>{error?.message}p>;
              })}
          div>
        )}
      />
      {(field) => (
          div>
            label htmlFor={field.name}>passwordlabel>
            input
              type="password"
              id={field.name}
              name={field.name}
              value={field.state.value}
              onChange={(e) => field.handleChange(e.target.value)}
            />
            {field.state.meta.isTouched &&
              field.state.meta.errors.map((error) => {
                return p>{error?.message}p>;
              })}
          div>
        )}
      />
      form.Subscribe
        selector={(state) => [state.canSubmit, state.isSubmitting]}
        children={([canSubmit, isSubmitting]) => (
          button type="submit" disabled={!canSubmit}>
            {isSubmitting ? "Submitting..." : "Submit"}
          button>
        )}
      />
    form>
  );
};

useForm()について

const form = useForm({
  defaultValues: {
    email: "",
    password: "",
  },
  onSubmit: ({ value }) => {
    console.log(value);
  },
  validators: {
    onChangeAsync: loginSchema,
    onChangeAsyncDebounceMs: 500,
  },
});

useForm()は以下で説明するdefaultValues等をインプットをもとに、formインスタンスを作成します。

defaultValues

defaultValuesは各フィールドの初期値を設定します。この例では、emailpasswordというフィールドを初期化し、空の文字列を設定しています。

onSubmit

onSubmitはサブミットされたときに実行される関数です。今回はコンソールを出力していますが、実際ではAPIリクエスト等の処理になります。

validators

validatorsはどのタイミングでバリデーションを行うかを設定します。

今回は非同期バリデーションを試したかったのでonChangeの非同期版であるonChangeAsyncを使用しています。またonChangeAsyncDebounceMsを設定することで500msでdebounceしてバリデーションされます。

今回使用したプロパティ以外も以下のようなプロパティがあります。

  • onBlur
  • onBlurAsync
  • onBlurAsyncDebounceMs
  • onMount
  • onSubmit
  • onSubmitAsync

https://tanstack.com/form/latest/docs/reference/interfaces/formvalidators

について

form.Field
  name="email"
  children={(field) => (
    div>
      label htmlFor={field.name}>Emaillabel>
      input
        type="email"
        id={field.name}
        name={field.name}
        value={field.state.value}
        onChange={(e) => field.handleChange(e.target.value)}
      />
      {field.state.meta.isTouched &&
        field.state.meta.errors.map((error) => {
          return p>{error?.message}p>;
       })}
    div>
  )}
/>

はテキストやチェックボックスなどの各入力フィールドを定義します。

今回はchildrenとして入力フォーム要素を受け取り、レンダリングしています。またfieldからnamestateを取得し各プロパティに設定します。

フィールドのアクセス状態について

各フィールドのアクセス状態についてはfield.stateの以下の値を参照します。

種別 アクセス状態
isTouched ユーザーがフィールドにタップ、タッチした後
isPristine ユーザーが値を変更するまで
isDirty ユーザーが値を変更した後

について

      form.Subscribe
        selector={(state) => [state.canSubmit, state.isSubmitting]}
        children={([canSubmit, isSubmitting]) => (
          button type="submit" disabled={!canSubmit}>
            {isSubmitting ? "Submitting..." : "Submit"}
          button>
        )}
      />

は、フォームの状態をsubscribeするコンポーネントです。

サブミット可能かどうかはcanSubmitで, サブミット中かどうかはisSubmittingをsubscribeし、ボタンの状態を制御しています。

はコンポーネントレベルの再レンダリングを引き起こしません。subscribeしている値が変更されるたびに、内の要素のみが再レンダリングされます。これにより、フォーム全体が再レンダリングされることなく、必要な部分だけを効率的に更新できるため、大規模なフォームでも高いパフォーマンスを維持できます。
以下のドキュメントを参照してください。

https://tanstack.com/form/latest/docs/framework/react/guides/reactivity

まとめ

TanStack Formは型安全で開発体験もよく、またシンプルでとても良いフォームライブラリだなと感じました。React Hook FormやConformではなくTanStack Formをどんどん使っていくのもありだなと思いました。また、TanStack Formには多様な機能が揃っているので、より深いレベルまで使いこなして、フォーム実装の可能性を最大限に引き出していきたいです!

個人的にTanStack系のライブラリは、色々と気になっているのでまた気に入ったライブラリがあればシェアできたらなと思います。

最後までお読みいただき、ありがとうございました!!

フラッグシティパートナーズ海外不動産投資セミナー 【DMM FX】入金

Source link