日本語

React Router v6の主要なナビゲーションパターンを探求。宣言的ルーティング、動的ルート、プログラムによるナビゲーション、ネストされたルート、データ読み込み戦略を学び、堅牢で使いやすいウェブアプリケーションを構築します。

React Router v6:現代のウェブアプリのためのナビゲーションパターンをマスターする

React Router v6は、Reactアプリケーション向けの強力で柔軟なルーティングライブラリです。ページ全体をリロードすることなくナビゲーションを管理することで、シームレスなユーザーエクスペリエンスを持つシングルページアプリケーション(SPA)を作成できます。このブログ記事では、React Router v6を使用した主要なナビゲーションパターンを掘り下げ、堅牢で使いやすいウェブアプリケーションを構築するための知識と例を提供します。

React Router v6のコアコンセプトを理解する

具体的なパターンに入る前に、いくつかの基本的なコンセプトを確認しましょう:

1. <Routes><Route>による宣言的ルーティング

React Router v6の基礎は宣言的ルーティングにあります。<Routes><Route>コンポーネントを使用してルートを定義します。<Routes>コンポーネントはルートのコンテナとして機能し、<Route>コンポーネントは特定のルートと、そのルートが現在のURLと一致した場合にレンダリングするコンポーネントを定義します。

例:基本的なルート設定

以下は、シンプルなアプリケーションのルートを設定する基本的な例です:


import { BrowserRouter, Routes, Route } from "react-router-dom";
import Home from "./pages/Home";
import About from "./pages/About";
import Contact from "./pages/Contact";

function App() {
  return (
    
      
        } />
        } />
        } />
      
    
  );
}

export default App;

この例では、3つのルートを定義しています:

BrowserRouterコンポーネントは、ブラウザの履歴に基づいたルーティングを可能にします。React Routerは現在のURLを定義されたルートと照合し、対応するコンポーネントをレンダリングします。

2. URLパラメータを使用した動的ルート

動的ルートを使用すると、URL内の異なる値を扱えるルートを作成できます。これは、商品IDやユーザーIDなどの一意の識別子に基づいてコンテンツを表示する場合に便利です。React Router v6では、:記号を使用してURLパラメータを定義します。

例:商品詳細の表示

eコマースアプリケーションがあり、各商品の詳細をIDに基づいて表示したいとします。次のように動的ルートを定義できます:


import { BrowserRouter, Routes, Route, useParams } from "react-router-dom";

function ProductDetails() {
  const { productId } = useParams();

  // productIdに基づいて商品詳細をフェッチする
  // ...

  return (
    

商品詳細

商品ID:{productId}

{/* ここに商品詳細を表示 */}
); } function App() { return ( } /> ); } export default App;

この例では:

国際化の例:言語コードの取り扱い

多言語サイトでは、言語コードを扱うために動的ルートを使用することがあります:


} />

このルートは、/en/about/fr/about/es/aboutのようなURLにマッチします。langパラメータは、適切な言語リソースを読み込むために使用できます。

3. useNavigateによるプログラムナビゲーション

宣言的ルーティングは静的なリンクには最適ですが、ユーザーのアクションやアプリケーションのロジックに基づいてプログラムでナビゲートする必要がしばしばあります。React Router v6は、この目的のためにuseNavigateフックを提供します。useNavigateは、異なるルートにナビゲートできる関数を返します。

例:フォーム送信後のリダイレクト

フォーム送信があり、フォームが正常に送信された後にユーザーを成功ページにリダイレクトしたいとします:


import { useNavigate } from "react-router-dom";

function MyForm() {
  const navigate = useNavigate();

  const handleSubmit = async (event) => {
    event.preventDefault();

    // フォームデータを送信する
    // ...

    // 送信成功後、成功ページにリダイレクトする
    navigate("/success");
  };

  return (
    
{/* フォームフィールド */}
); } export default MyForm;

この例では:

ナビゲーション中のState渡し

navigateの2番目の引数を使用して、ナビゲーションと共にstateを渡すこともできます:


navigate("/confirmation", { state: { orderId: "12345" } });

これにより、ターゲットコンポーネントにデータを渡すことができ、そのデータはuseLocationフックを使用してアクセスできます。

4. ネストされたルートとレイアウト

ネストされたルートを使用すると、あるルートが別のルート内にネストされる階層的なルーティング構造を作成できます。これは、複数のナビゲーションレベルを持つ複雑なアプリケーションを整理するのに便利です。これにより、アプリケーションの特定のセクション全体で特定のUI要素が一貫して表示されるレイアウトを作成するのに役立ちます。

例:ユーザープロフィールセクション

ユーザープロフィールセクションがあり、ユーザーのプロフィール情報、設定、注文を表示するためのネストされたルートがあるとします:


import { BrowserRouter, Routes, Route, Link } from "react-router-dom";

function Profile() {
  return (
    

ユーザープロフィール

  • プロフィール情報
  • 設定
  • 注文
} /> } /> } />
); } function ProfileInformation() { return

プロフィール情報コンポーネント

; } function Settings() { return

設定コンポーネント

; } function Orders() { return

注文コンポーネント

; } function App() { return ( } /> ); } export default App;

この例では:

親ルートの*は非常に重要です。これは親ルートが任意のサブパスにマッチすることを示し、ネストされたルートがProfileコンポーネント内で正しくマッチされるようにします。

5. 「Not Found」(404)エラーの処理

ユーザーが存在しないルートにナビゲートした場合のケースを処理することが不可欠です。React Router v6では、キャッチオールルートでこれを簡単に行えます。

例:404ページの実装


import { BrowserRouter, Routes, Route, Link } from "react-router-dom";

function NotFound() {
  return (
    

404 - Not Found

お探しのページは見つかりませんでした。

ホームに戻る
); } function App() { return ( } /> } /> } /> ); }

この例では:

6. React Router v6でのデータ読み込み戦略

React Router v6には、以前のバージョン(`useRouteMatch`を持つReact Router v5)のような組み込みのデータ読み込みメカニズムは含まれていません。しかし、さまざまなデータ読み込み戦略を効果的に実装するためのツールを提供しています。

選択肢1:コンポーネント内でのデータフェッチ

最も簡単なアプローチは、ルートをレンダリングするコンポーネント内で直接データをフェッチすることです。useEffectフックを使用して、コンポーネントがマウントされたときやURLパラメータが変更されたときにデータをフェッチできます。


import { useParams } from "react-router-dom";
import { useEffect, useState } from "react";

function ProductDetails() {
  const { productId } = useParams();
  const [product, setProduct] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    async function fetchProduct() {
      try {
        const response = await fetch(`/api/products/${productId}`);
        if (!response.ok) {
          throw new Error(`HTTPエラー! status: ${response.status}`);
        }
        const data = await response.json();
        setProduct(data);
        setLoading(false);
      } catch (e) {
        setError(e);
        setLoading(false);
      }
    }

    fetchProduct();
  }, [productId]);

  if (loading) return 

読み込み中...

; if (error) return

エラー: {error.message}

; if (!product) return

商品が見つかりません

; return (

{product.name}

{product.description}

); } export default ProductDetails;

このアプローチは単純明快ですが、複数のコンポーネントでデータをフェッチする必要がある場合にコードの重複につながる可能性があります。また、コンポーネントがマウントされた後にのみデータフェッチが開始されるため、効率は低くなります。

選択肢2:データフェッチ用のカスタムフックの使用

コードの重複を減らすために、データフェッチロジックをカプセル化するカスタムフックを作成できます。このフックは、複数のコンポーネントで再利用できます。


import { useState, useEffect } from "react";

function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    async function fetchData() {
      try {
        const response = await fetch(url);
        if (!response.ok) {
          throw new Error(`HTTPエラー! status: ${response.status}`);
        }
        const json = await response.json();
        setData(json);
        setLoading(false);
      } catch (e) {
        setError(e);
        setLoading(false);
      }
    }

    fetchData();
  }, [url]);

  return { data, loading, error };
}

export default useFetch;

そして、このフックをコンポーネントで使用できます:


import { useParams } from "react-router-dom";
import useFetch from "./useFetch";

function ProductDetails() {
  const { productId } = useParams();
  const { data: product, loading, error } = useFetch(`/api/products/${productId}`);

  if (loading) return 

読み込み中...

; if (error) return

エラー: {error.message}

; if (!product) return

商品が見つかりません

; return (

{product.name}

{product.description}

); } export default ProductDetails;

選択肢3:データフェッチ機能を備えたルーティングライブラリの使用(TanStack Router, Remix)

TanStack RouterRemixのようなライブラリは、ルーティングとシームレスに統合された組み込みのデータフェッチメカニズムを提供します。これらのライブラリは、しばしば次のような機能を提供します:

このようなライブラリを使用すると、特に複雑なアプリケーションにおいて、データ読み込みを大幅に簡素化し、パフォーマンスを向上させることができます。

サーバーサイドレンダリング(SSR)と静的サイト生成(SSG)

SEOと初期読み込みパフォーマンスを向上させるには、Next.jsやGatsbyのようなフレームワークでSSRまたはSSGを使用することを検討してください。これらのフレームワークを使用すると、サーバー上またはビルド時にデータをフェッチし、事前にレンダリングされたHTMLをクライアントに提供できます。これにより、クライアントが初期読み込み時にデータをフェッチする必要がなくなり、より高速でSEOに優しいエクスペリエンスが実現します。

7. 異なるルータータイプの操作

React Router v6は、さまざまな環境やユースケースに合わせて、異なるルーター実装を提供しています:

アプリケーションの要件と環境に最も適したルータータイプを選択してください。

結論

React Router v6は、Reactアプリケーション向けの包括的で柔軟なルーティングソリューションを提供します。このブログ記事で説明したナビゲーションパターンを理解し、適用することで、堅牢で、ユーザーフレンドリーで、保守しやすいウェブアプリケーションを構築できます。<Routes><Route>による宣言的ルーティングから、URLパラメータを使用した動的ルート、useNavigateによるプログラムナビゲーション、そして効果的なデータ読み込み戦略まで、React Router v6はユーザーにシームレスなナビゲーション体験を創造する力を与えます。さらなる制御とパフォーマンス最適化のために、高度なルーティングライブラリやSSR/SSGフレームワークの探求を検討してください。これらのパターンを特定のアプリケーション要件に合わせて適応させ、常に明確で直感的なユーザーエクスペリエンスを優先することを忘れないでください。