🧬

【2024年】React Router & TanStack Router比較

2024/05/23に公開

先日RemixとReact Routerがマージされ、React Routerv7で統合されるという衝撃的なニュースがありました。

https://remix.run/blog/merging-remix-and-react-router

React RouterはこれまでSPA開発におけるルーティングライブラリとして広く活躍してきました。しかし今回の統合の背景にReact Router自体がすでにRemixとの差分が無くなっていたという事情があったことからも分かる通り、React Routerは直感的にルーティングの域を超えた存在です。

またFrameworkを採用せずReactでSPA開発をする際にはReact Routerの存在は大きいものでしたが、昨年の12月にTanStack Routerがversion1系になったことでVite+Reactでルーティングを実装する選択肢が新たに1つ増えました。

TanStack Routerについてご存知でない方はこちらの記事で詳しく紹介をしていますので、ぜひご覧ください!

https://zenn.dev/aishift/articles/ad1744836509dd

はじめに

ここでは、TanStack Routerのドキュメントにある「Comparison | TanStack Router vs React Router」の項目を参考にして比較を行います。

https://tanstack.com/router/latest/docs/framework/react/comparison

また、記事執筆を行なっている2024年5月時点では以下のバージョンを前提としています。

  • React Router
    • v6.23.1
  • TanStack Router
    • v1.32.12

簡潔な結論

早速ですが、React RouterとTanStack Routerの双方の比較について簡潔に結論を述べます。

React Routerは、Web標準に準拠したFull-Stackなライブラリであり、データの更新も含めたData Flowを持っている。

TanStack Routerは、開発者体験に秀でたルーティングライブラリであり、責務がルーティングに限定されるため、TanStack Queryのようなライブラリと相性が良い。

これらがどのような理解であるかをこれから解説します。

共通している特徴

まずは共通している特徴について整理します。

React Router TanStack Router
File-Based Routing
Nested / Layout Routes
Route Prefetching
Router Loaders
Path Params
Search Param Hooks

双方でFile-Basedなルーティングを実装することができる[1]上、基本的なルーティングに必要な要素は全て兼ね備えています。また、<Outlet />を用いたNested Routesの実装方法が似ていることから、どちらかに精通している方であればすんなり理解ができるかと思います。

また各Routeに紐づくloaderAPIでデータ取得ができることができるのは、リクエストのwaterfallを防ぐ上でも重要な機能です。

そして実装方法は大きく異なりますが、どちらもPath PramsとSearch Paramを効率的に扱えるAPIを提供してくれています。

では、次にどのような違いがあるのかを整理していきます。

Web標準に準拠したReact Routerと開発者体験に秀でたTanStack Router

React RouterがWeb標準に準拠していることはRemixのドキュメントなどからも理解することができます。Web標準に従うメリットは、常に進化するWebの新しい技術やベストプラクティスに適応しやすくなり、中長期的な保守性が向上することが一つにあると思います。

https://www.learnremix.io/

その一方でTanStack Routerは、特に開発者体験(DX)を意識した実装をしており、Web標準に近い実装になっているとは言えませんが、型安全性やValidationなど効率的にルーティングを実装できる機構を持っています。

両者の特徴が色濃く出ているのがSearch Paramsの扱いです。

React Routerで扱うSearch Params

React RouterではURLSearchParamsを効率的に扱えるHooks(useSearchParams)を用いてSearch Paramsを操作します。

// searchParams is a URLSearchParams object.
// See https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams
let [searchParams, setSearchParams] = useSearchParams();

function handleSubmit(event) {
  event.preventDefault();

  let params = serializeFormQuery(event.target);
  setSearchParams(params);
}

useSearchParamsから返却されるsearchParamsはURLSearchParamsのObjectなので、普段Webに慣れている方であればスムーズに実装することができるでしょう。

https://reactrouter.com/en/main/hooks/use-search-params#usesearchparams

こちらがReact RouterのSearch Pramsに関係するソースコードの一部です。createSearchParams関数でURLSearchParamsのObjectを作成しています。

https://github.com/remix-run/react-router/blob/main/packages/react-router-dom/dom.ts#L76-L91

一方でTanStack Routerの実装を見てみましょう。すると、どこにもURLSearchParamsを使用している実装が見当たりません。TanStack Routerでは独自でSearch Paramsを扱うための実装をしています。その理由は次に紹介します。

https://github.com/TanStack/router/blob/v1.33.5/packages/react-router/src/router.ts#L1197-L1208

https://github.com/TanStack/router/blob/v1.33.5/packages/react-router/src/router.ts#L1219-L1227

当然TanStack Routerで扱えるSearch PramsのObjectはURLSearchParamsではないため、TanStack RouterでSearch Paramsを実装する方法を調べ、設計を考えるコストが生まれます。この点でReact RouterがWeb標準に準拠しているという特徴は、あらゆる開発者が学習コストを抑えて実装ができるというメリットがあると考えます。

TanStack Routerで扱うSearch Params

では、なぜTanStack Routerは独自でSearch Paramsを実装しているのでしょうか?TanStack Routerの特徴は「Search Paramsを状態管理として効率的に使用できる」ことです。

ユーザ目線で考えるとSearch Paramsを状態管理として扱うことで以下のようなUXの向上が見込めます。

  • Search Paramsの状態を保ったままブックマークが可能
  • Search Paramsの状態を保ったままユーザ同士で共有可能
  • リロードしても状態が保持される

そのため、適切なケースにおいてSearch Paramsを積極的に用いて状態管理することには大きなメリットがあります。

なぜURLSearchParamsでは不十分なのか?

https://tanstack.com/router/latest/docs/framework/react/guide/search-params

URLSearchParams APIを扱う際には、下記の課題が伴います。

  • 常にstring型である。
  • flatな構造である。
  • search paramsの更新時にURL pathnameを考慮する必要がある。

しかし開発者目線で考えると、Search Paramsも状態管理ライブラリのように多様な型を扱い、ネストされた配列やオブジェクトなど複雑なデータ型を操作したいはずです。

そしてこれらを解決しているのがTanStack Routerです。

TanStack Routerを採用すると下の例のようにJson-Firstで型安全に複雑なデータ型を扱うことができます。さらにはzodと組み合わせたSearch ParamsのValidationなども実装することができます。

const link = (
  <Link
    to="/shop"
    search={{
      pageIndex: 3,
      includeCategories: ['electronics', 'gifts'],
      sortBy: 'price',
      desc: true,
    }}
  />
)
// → /shop?pageIndex=3&includeCategories=%5B%22electronics%22%2C%22gifts%22%5D&sortBy=price&desc=true

整理

まず、React RouterとTanStack Routerの特徴を比較するために、Search Paramsの扱いについて着目しました。すると、React RouterはWeb標準に準拠した実装になっている一方で、TanStack RouterはURLSearchParamsの課題を解決し、効率的に状態管理できる実装をしていることがわかります。

React Routerのメリット

  • Search ParamsでURLSearchParamsを扱えるので、学習コストを抑えてWeb標準に準拠した実装ができる

TanStack Routerのメリット

  • Json-Firstで型安全かつ複雑な構造が扱え、validationもできるため効率よくSearch Paramsを実装し、ユーザ体験を向上させることができる。

Full-Stack APIを持つReact Routerとルーティングのみに責務を持つTanStack Router

React RouterとTanStack Routerで大きく異なる点として、ActionsFormAPIの存在があります。冒頭でReact RouterはRemixとの差分がなくなっていると説明しましたが、React Routerはルーティングライブラリでありながらデータの更新に関する責務も持っています。

https://reactrouter.com/en/main/components/form

React RouterのData Flow

こちらはRemixのData Flowの図式ですがReact Routerについても同様の説明ができます。

https://remix.run/blog/remix-data-flow

Loaderでデータを取得 ⇨ Componentを描画 ⇨ Actionでデータを更新 ⇨ 再度Loaderでデータを取得し、Componentを描画といった流れです。

React Router Sample
<Route
  path="/projects/:id"
  element={<Project />}
  loader={async ({ params }) => {
    return fakeLoadProject(params.id);
  }}
  action={async ({ request, params }) => {
    switch (request.method) {
      case "PUT": {
        let formData = await request.formData();
        let name = formData.get("projectName");
        return fakeUpdateProject(name);
      }
    }
  }}
/>;

function Project() {
  let project = useLoaderData();

  return (
    <Form method="put">
      <input
        type="text"
        name="projectName"
        defaultValue={project.name}
      />
      <button type="submit">Update Project</button>
    </Form>
  );
}

このData FlowがReact Routerを採用する時点で組み込まれています。そのため、その他追加のライブラリを選定することなくData Flowに沿ったアプリケーションの実装を一貫して行うことができます。

TanStack Routerでデータ更新する方法

ではTanStack Routerの場合を整理します。下の例ではあえてCode Basedのルーティングを例にしていますが、React Routerと比較した際の違いとして、Actionsがないことがわかります。

TanStack Router Sample
const invoiceRoute = createRoute({
  getParentRoute: () => invoicesRoute,
  path: '$invoiceId',
  loader: ({ params: { invoiceId } }) => fetchInvoiceById(invoiceId),
  component: InvoiceComponent,
})

補足としてTanStack Routerのドキュメントに”Data Mutations”の項目がありますが、これはLoaderキャッシュを更新する処理の説明であり、データ更新処理自体を管理するものではないという解釈をしています。

https://tanstack.com/router/latest/docs/framework/react/guide/data-mutations

TanStack Queryと組み合わせる

では、Mutation API(Actionsなど)を持たないTanStack Routerにおいてどのようにデータ更新処理を実装するのでしょうか?ここではTanStack Queryのようなサーバデータの状態管理ライブラリを採用することを一つの選択肢として紹介します。

TanStack Queryに関する内容は下の記事にて解説をしています。

https://zenn.dev/taisei_13046/books/133e9995b6aadf

TanStack QueryとTanStack Routerは同じTanStack製のライブラリであることからも分かるとおり、相性がとても良いため容易に統合させることができます。このように、TanStack Routerはあくまでルーティングに責務を持っていることから、別ライブラリとの組み合わせが必要になります。

整理

この章の内容をまとめると下の表のようになります。

React Router TanStack Router
ルーティング
データ取得(Loader)
データ更新(Mutation) 🛑 (TanStack Queryと組み合わせる)

*ここではデータ取得(Loader)もルーティングの責務と仮定して説明します。

React Routerはルーティングだけでなくデータの更新も含めたData Flowを持っているのに対してTanStack Routerはルーティングのみに責務を持っていることがわかります。

React Routerを採用することで、追加のライブラリを採用することなくアプリケーションを開発できますが、TanStack Routerでは更新も含めたデータの状態を管理するライブラリを別途選定する必要があります。

この点React Routerはそれ自体でアプリケーションの実装ができるメリットがありますが、React RouterがよりFull-Stack化するにつれ、ルーティングのみに集中しているライブラリがなくなっていた現状がありました。そのためTanStack Routerは重要な存在だと考えています。

例えばReact RouterとTanStack Queryを組み合わせた場合、それぞれで役割が重複してしまいます。そのためReact Routerを採用した場合には、そのData Flowに従うのが素直だと思います。

ですがアプリケーションの要件によっては、TanStack Queryを用いてより柔軟にサーバデータを扱いたいケースも考えられます。

その場合はTanStack Routerのようにルーティングのみに責務を持っているライブラリの方が相性がいいでしょう。

まとめ

今回はReact RouterとTanStack Routerの特徴を比較して双方の持つ責務を整理しました。

React Routerは、Web標準に準拠したFull-Stackなライブラリであり、データの更新も含めたData Flowを持っている。

TanStack Routerは、開発者体験に秀でたルーティングライブラリであり、責務がルーティングに限定されるため、TanStack Queryのようなライブラリと相性が良い。

どちらも異なる強みがあり優劣を付けられるものではありませんが、技術選定をする際にはこれらの違いを理解した上で採用できると良いなと思います。

最後に

AI Shiftではエンジニアの採用に力を入れています!
少しでも興味を持っていただけましたら、カジュアル面談でお話しませんか?
(オンライン・19時以降の面談も可能です!)

【面談フォームはこちら】

https://hrmos.co/pages/cyberagent-group/jobs/1826557091831955459

脚注
  1. TanStack Routerのドキュメントでは、React RouterでもFile-Basedなルーティングができると記載されていました。React RouterのドキュメントではCode Basedなルーティングが基本とされているようなので、generoutedのような別ライブラリの使用やRemix SPAを想定しているかと思います。 ↩︎

AI Shift Tech Blog

Discussion