Open4

Tanstack Router のドキュメントの LLMへの会話ログ

yuji⛩️yuji⛩️

ネストされたLayout

ネストされたLayoutを使用することで、アプリケーションの構造を整理し、共通のUI要素を複数のルートに適用することができます。以下に、TanStack Routerを使用したネストされたLayoutの詳細な実装方法を説明します。

ネストされたLayout
  1. ルートの定義:
    まず、ネストされたルートを定義します。親ルートにはLayoutコンポーネントを指定し、その中に子ルートを配置します。

    import { createFileRoute } from '@tanstack/react-router';
    import ParentLayout from './ParentLayout';
    import ChildComponent from './ChildComponent';
    
    export const ParentRoute = createFileRoute('/parent')({
      component: ParentLayout,
      children: [
        {
          path: 'child',
          component: ChildComponent,
        },
      ],
    });
    
  2. 親Layoutコンポーネントの作成:
    親Layoutコンポーネントでは、共通のUI要素を定義し、Outletを使用して子ルートをレンダリングします。

    import { Outlet } from '@tanstack/react-router';
    
    function ParentLayout() {
      return (
        <div>
          <h1>親レイアウト</h1>
          {/* 子ルートがここにレンダリングされます */}
          <Outlet />
        </div>
      );
    }
    
    export default ParentLayout;
    
  3. 子コンポーネントの作成:
    子ルートに対応するコンポーネントを作成します。このコンポーネントは、親Layoutの中で表示されます。

    function ChildComponent() {
      return <div>子コンポーネントの内容</div>;
    }
    
    export default ChildComponent;
    
  4. ルーターへの統合:
    ルートツリーに親ルートを追加し、アプリケーション全体のルーティングを設定します。

    import { createRouter } from '@tanstack/react-router';
    import { ParentRoute } from './ParentRoute';
    
    const routeTree = {
      children: [
        ParentRoute,
        // 他のルート
      ],
    };
    
    export const router = createRouter({
      routeTree,
    });
    
  5. アプリケーションの設定:
    最後に、アプリケーションを設定し、ルーターを使用してレンダリングします。

    import React from 'react';
    import { RouterProvider } from '@tanstack/react-router';
    import { router } from './router';
    
    function App() {
      return (
        <RouterProvider router={router} />
      );
    }
    
    export default App;
    

この実装により、/parent/childのURLにアクセスすると、親レイアウトが表示され、その中に子コンポーネントがレンダリングされます。これにより、共通のUI要素を持つネストされたルートを簡単に管理できます。

yuji⛩️yuji⛩️

Protected Route

TanStack Routerを使用して、保護されたレイアウトを作成する方法について説明します。保護されたレイアウトは、ユーザーが認証されている場合にのみアクセスできるようにするためのものです。以下の手順で設定できます。ここでは、認証状態を確認するためのloaderを使用し、ユーザーが認証されていない場合にリダイレクトする方法を示します。

保護されたレイアウトについて

ファイル構成

src/
├── components/
│   ├── AuthProvider.js
│   ├── ProtectedLayout.js
│   └── Login.js
├── routes/
│   ├── _pathless.js
│   ├── dashboard.js
│   └── settings.js
└── App.js

各ファイルの詳細

  1. AuthProvider.js
    認証状態を管理するためのコンテキストを提供します。

    import React, { createContext, useContext, useState } from 'react';
    
    const AuthContext = createContext();
    
    export const AuthProvider = ({ children }) => {
      const [auth, setAuth] = useState({ isAuthenticated: false });
    
      return (
        <AuthContext.Provider value={{ auth, setAuth }}>
          {children}
        </AuthContext.Provider>
      );
    };
    
    export const useAuth = () => useContext(AuthContext);
    
  2. ProtectedLayout.js
    loaderを使用して認証状態を確認し、未認証の場合はログインページにリダイレクトします。

    import { Outlet, redirect } from '@tanstack/react-router';
    import { useAuth } from '../components/AuthProvider';
    
    export const loader = async () => {
      const { auth } = useAuth();
      if (!auth.isAuthenticated) {
        return redirect('/login');
      }
      return null; // 認証されている場合は何も返さない
    };
    
    function ProtectedLayout() {
      return (
        <div>
          <h1>保護されたレイアウト</h1>
          <Outlet />
        </div>
      );
    }
    
    export default ProtectedLayout;
    
  3. dashboard.js
    保護されたダッシュボードコンポーネントです。

    function Dashboard() {
      return <div>ダッシュボードの内容</div>;
    }
    
    export default Dashboard;
    
  4. settings.js
    保護された設定コンポーネントです。

    function Settings() {
      return <div>設定の内容</div>;
    }
    
    export default Settings;
    
  5. login.js
    ログインページのコンポーネントです。

    import { useAuth } from '../components/AuthProvider';
    
    function Login() {
      const { setAuth } = useAuth();
    
      const handleLogin = () => {
        // 認証処理を実装
        setAuth({ isAuthenticated: true });
      };
    
      return (
        <div>
          <h1>ログイン</h1>
          <button onClick={handleLogin}>ログイン</button>
        </div>
      );
    }
    
    export default Login;
    
  6. _pathless.js
    保護されたルートを定義し、ProtectedLayoutを使用します。

    import { createFileRoute } from '@tanstack/react-router';
    import ProtectedLayout, { loader as protectedLoader } from '../components/ProtectedLayout';
    import Dashboard from './dashboard';
    import Settings from './settings';
    import Login from './login';
    
    export const ProtectedRoute = createFileRoute('/_protected')({
      component: ProtectedLayout,
      loader: protectedLoader,
    });
    
    export const LoginRoute = createFileRoute('/login')({
      component: Login,
    });
    
  7. App.js
    アプリケーション全体を設定し、ルーターを使用してレンダリングします。

    import React from 'react';
    import { RouterProvider } from '@tanstack/react-router';
    import { AuthProvider } from './components/AuthProvider';
    import { ProtectedRoute, LoginRoute } from './routes/_pathless';
    
    function App() {
      return (
        <AuthProvider>
          <RouterProvider router={ProtectedRoute} />
          <RouterProvider router={LoginRoute} />
        </AuthProvider>
      );
    }
    
    export default App;
    

この実装により、/_protected/dashboard/_protected/settingsのURLにアクセスすると、ProtectedLayoutのloaderが実行され、認証状態が確認されます。未認証の場合は、ログインページにリダイレクトされます。認証されている場合は、ダッシュボードや設定コンポーネントが表示されます。

yuji⛩️yuji⛩️

path less route

パスレスルートは、特定のパスを持たないルートで、主にレイアウトや共通の機能を複数の子ルートに適用するために使用されます。これにより、コードの重複を避け、アプリケーションの構造を整理することができます。

以下に、パスレスルートの設定方法を詳しく説明します。

パスレスルートについて
  1. パスレスルートの作成:
    パスレスルートは、通常のルートと同様に定義しますが、パスを指定しません。代わりに、レイアウトコンポーネントを指定します。

    import { createFileRoute } from '@tanstack/react-router';
    import LayoutComponent from './LayoutComponent';
    
    export const PathlessRoute = createFileRoute('/_pathless')({
      component: LayoutComponent,
    });
    
  2. レイアウトコンポーネントの作成:
    レイアウトコンポーネントは、共通のUI要素や機能を含むコンポーネントです。この中でOutletを使用して、子ルートをレンダリングします。

    import { Outlet } from '@tanstack/react-router';
    
    function LayoutComponent() {
      return (
        <div>
          <h1>共通のヘッダー</h1>
          {/* 子ルートがここにレンダリングされます */}
          <Outlet />
        </div>
      );
    }
    
    export default LayoutComponent;
    
  3. ルーターへの統合:
    パスレスルートをルートツリーに追加し、他のルートと組み合わせて使用します。

    import { createRouter } from '@tanstack/react-router';
    import { PathlessRoute } from './PathlessRoute';
    
    const routeTree = {
      children: [
        PathlessRoute,
        // 他のルート
      ],
    };
    
    export const router = createRouter({
      routeTree,
    });
    

このようにして、パスレスルートを使用することで、共通のレイアウトや機能を簡単に管理し、アプリケーションの構造をより効率的に設計することができます。

yuji⛩️yuji⛩️

beforeLoad を利用して認証チェックしてリダイレクトする

export const Route = createFileRoute('/_auth')({
  beforeLoad: ({ context, location }) => {
    if (!context.auth.isAuthenticated) {
      throw redirect({
        to: '/login',
        search: {
          redirect: location.href,
        },
      })
    }
  },
  component: AuthLayout,
})

beforeLoad の利用例

beforeLoad は、ルートが読み込まれる前に実行される関数を指定するために使用されます。この関数は、ルートのローダー関数と同じ引数を受け取ります。以下に、beforeLoad の実装例を示します。

まず、ルートのコンテキストを作成し、fetchPosts 関数を定義します。

beforeLoad の利用例
// /utils/fetchPosts.tsx
export const fetchPosts = async () => {
  const res = await fetch(`/api/posts?page=${pageIndex}`);
  if (!res.ok) throw new Error('Failed to fetch posts');
  return res.json();
};

次に、ルートを作成し、beforeLoad を使用してコンテキストを設定します。

// /routes/__root.tsx
import { createRootRouteWithContext } from '@tanstack/react-router';

export const Route = createRootRouteWithContext<{
  fetchPosts: typeof fetchPosts;
}>()();

次に、特定のルートで beforeLoad を使用して、データを取得するローダーを定義します。

// /routes/posts.tsx
import { createFileRoute } from '@tanstack/react-router';

export const Route = createFileRoute('/posts')({
  beforeLoad: async ({ context }) => {
    // ここで必要なデータを取得することができます
    const posts = await context.fetchPosts();
    return { posts }; // 取得したデータを返す
  },
  loader: ({ context }) => context.posts, // 取得したデータをローダーで使用
});

このように、beforeLoad を使用することで、ルートが読み込まれる前に必要なデータを取得し、そのデータをルートのローダーで利用することができます。これにより、データの依存性を管理しやすくなります。