ガシャポン攻略法:TanStack RouterでReactのルーティングを制覇する
TanStack Routerは、Reactアプリケーションでルーティングを管理するための、強力で型安全なライブラリです。単にページを切り替えるだけでなく、もっと複雑な要件にも対応できる、まさに「ガシャポンの攻略法」のようなライブラリです。
従来のルーターと比べて、TanStack Routerには以下のような利点があります。
完全な型安全性
URLのパス、検索パラメータ、ルートパラメータなどがすべて型付けされます。これにより、意図しないURLにアクセスするなどのバグを未然に防ぐことができます。
強力なキャッシュ機能
サーバーから取得したデータを自動的にキャッシュします。同じデータに再度アクセスする際に、ネットワークリクエストを削減でき、パフォーマンスが大幅に向上します。
ファーストクラスの検索パラメータAPI
検索パラメータ(?name=taro&age=30など)の読み書きが非常に簡単になります。複雑なクエリにも対応できます。
クライアントサイドキャッシュとの連携
React Queryのようなクライアントサイドキャッシュライブラリとシームレスに連携できます。これにより、データの取得と表示をより効率的に行えます。
アイソモーフィックレンダリング
サーバーサイドレンダリング(SSR)とクライアントサイドレンダリングの両方に対応しています。これにより、SEOの向上や初期表示の高速化が期待できます。
TanStack Routerの導入は簡単です。まずは、npmまたはyarnでパッケージをインストールします。
npm install @tanstack/router-core @tanstack/react-router
# または
yarn add @tanstack/router-core @tanstack/react-router
次に、アプリケーションのルートファイル(main.tsxなど)でルーターを設定します。
// src/main.tsx
import ReactDOM from 'react-dom/client'
import {
Router,
Route,
RootRoute
} from '@tanstack/react-router'
const rootRoute = new RootRoute()
const indexRoute = new Route({
getParentRoute: () => rootRoute,
path: '/',
component: () => (
<div>
<h3>Welcome Home!</h3>
</div>
),
})
const aboutRoute = new Route({
getParentRoute: () => rootRoute,
path: '/about',
component: () => <div>Hello from About!</div>,
})
const routeTree = rootRoute.addChildren([indexRoute, aboutRoute])
const router = new Router({ routeTree })
declare module '@tanstack/react-router' {
interface Register {
router: typeof router
}
}
ReactDOM.createRoot(
document.getElementById('root')!
).render(
<Router router={router} />
)
これで、基本的なルーティングの準備ができました。
ここでは、ユーザーのIDに応じて異なる詳細ページを表示する例を見てみましょう。
まず、ユーザーIDをパラメータとして受け取るルートを定義します。
import {
Router,
Route,
RootRoute,
Outlet,
Link,
RouterProvider
} from '@tanstack/react-router'
// ユーザーリストを表示するコンポーネント
function UsersIndex() {
return (
<>
<p>ユーザー一覧</p>
<ul>
<li>
<Link to="/users/$userId" params={{ userId: '1' }}>
Taro
</Link>
</li>
<li>
<Link to="/users/$userId" params={{ userId: '2' }}>
Hanako
</Link>
</li>
</ul>
<Outlet />
</>
)
}
// ユーザー詳細を表示するコンポーネント
const UserDetails = () => {
const { userId } = Route.useParams()
return (
<div>
<h2>ユーザー詳細</h2>
<p>ID: {userId}</p>
</div>
)
}
// ルートツリーの定義
const rootRoute = new RootRoute({
component: () => (
<>
<div style={{ display: 'flex', gap: '1rem' }}>
<Link to="/">Home</Link>
<Link to="/users">Users</Link>
</div>
<Outlet />
</>
),
})
const usersRoute = new Route({
getParentRoute: () => rootRoute,
path: 'users',
component: UsersIndex,
})
const userIdRoute = new Route({
getParentRoute: () => usersRoute,
path: '$userId',
component: UserDetails,
})
const indexRoute = new Route({
getParentRoute: () => rootRoute,
path: '/',
component: () => <div>Hello!</div>,
})
const routeTree = rootRoute.addChildren([indexRoute, usersRoute.addChildren([userIdRoute])])
const router = new Router({ routeTree })
declare module '@tanstack/react-router' {
interface Register {
router: typeof router
}
}
function App() {
return <RouterProvider router={router} />
}
export default App
この例では、$userIdという動的なパラメータを持つルートを定義しています。Linkコンポーネントを使って、このルートに遷移する際にparamsプロパティでユーザーIDを渡しています。
TanStack Routerは、ルートごとにデータをフェッチする機能も持っています。これにより、コンポーネントがレンダリングされる前に必要なデータをロードし、キャッシュしておくことができます。
import {
Router,
Route,
RootRoute,
Outlet,
Link,
RouterProvider,
useMatch,
} from '@tanstack/react-router'
// ユーザーのデータを非同期で取得する関数
const fetchUser = async (userId: string) => {
console.log(`fetching user with ID: ${userId}`)
// 実際のAPIコールをシミュレート
return new Promise(resolve =>
setTimeout(() => {
resolve({
id: userId,
name: `User ${userId}`,
email: `user${userId}@example.com`,
})
}, 1000)
)
}
// ユーザー詳細を表示するコンポーネント
const UserDetails = () => {
// `useMatch`でルートに紐づくデータを取得
const { data } = useMatch({ strict: false })
return (
<div>
<h2>ユーザー詳細</h2>
<p>ID: {data.id}</p>
<p>名前: {data.name}</p>
<p>メール: {data.email}</p>
</div>
)
}
// データフェッチを定義
const userIdRoute = new Route({
getParentRoute: () => usersRoute,
path: '$userId',
component: UserDetails,
// loaderでデータをフェッチ
loader: async ({ params }) => {
return await fetchUser(params.userId)
},
})
// 他のルートツリーの定義は上記と同様...
loaderプロパティを使うことで、コンポーネントが表示される前にfetchUser関数が実行されます。一度ロードされたデータはキャッシュされるため、同じユーザーIDに再度アクセスしても、ネットワークリクエストは発生しません。これにより、ユーザー体験が向上します。