React Router v6の主要なナビゲーションパターンを探求。宣言的ルーティング、動的ルート、プログラムによるナビゲーション、ネストされたルート、データ読み込み戦略を学び、堅牢で使いやすいウェブアプリケーションを構築します。
React Router v6:現代のウェブアプリのためのナビゲーションパターンをマスターする
React Router v6は、Reactアプリケーション向けの強力で柔軟なルーティングライブラリです。ページ全体をリロードすることなくナビゲーションを管理することで、シームレスなユーザーエクスペリエンスを持つシングルページアプリケーション(SPA)を作成できます。このブログ記事では、React Router v6を使用した主要なナビゲーションパターンを掘り下げ、堅牢で使いやすいウェブアプリケーションを構築するための知識と例を提供します。
React Router v6のコアコンセプトを理解する
具体的なパターンに入る前に、いくつかの基本的なコンセプトを確認しましょう:
- 宣言的ルーティング: React Routerは宣言的なアプローチを採用しており、ルートをReactコンポーネントとして定義します。これにより、ルーティングロジックが明確で保守しやすくなります。
- コンポーネント: コアコンポーネントには、
BrowserRouter
、HashRouter
、MemoryRouter
、Routes
、Route
などがあります。 - フック: React Routerは、
useNavigate
、useLocation
、useParams
、useRoutes
などのフックを提供し、ルーティング情報へのアクセスやナビゲーションの操作を可能にします。
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つのルートを定義しています:
/
:Home
コンポーネントをレンダリングします。/about
:About
コンポーネントをレンダリングします。/contact
:Contact
コンポーネントをレンダリングします。
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;
この例では:
/products/:productId
は、:productId
がURLパラメータである動的ルートを定義します。useParams
フックは、ProductDetails
コンポーネント内でproductId
パラメータの値にアクセスするために使用されます。- その後、
productId
を使用してデータソースから対応する商品詳細をフェッチできます。
国際化の例:言語コードの取り扱い
多言語サイトでは、言語コードを扱うために動的ルートを使用することがあります:
} />
このルートは、/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;
この例では:
useNavigate
フックを使用してnavigate
関数を取得します。- フォームが正常に送信された後、
navigate("/success")
を呼び出してユーザーを/success
ルートにリダイレクトします。
ナビゲーション中の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/*
ルートは、/profile
で始まる任意のURLにマッチします。Profile
コンポーネントは、ナビゲーションメニューと、ネストされたルートを処理するための<Routes>
コンポーネントをレンダリングします。- ネストされたルートは、
/profile/info
、/profile/settings
、/profile/orders
に対してレンダリングするコンポーネントを定義します。
親ルートの*
は非常に重要です。これは親ルートが任意のサブパスにマッチすることを示し、ネストされたルートが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 (
} />
} />
} />
);
}
この例では:
<Route path="*" element={<NotFound />} />
ルートは、他の定義されたどのルートにもマッチしない任意のURLにマッチするキャッチオールルートです。- 他のルートがマッチしない場合にのみマッチするように、このルートを
<Routes>
コンポーネントの最後に配置することが重要です。
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 RouterやRemixのようなライブラリは、ルーティングとシームレスに統合された組み込みのデータフェッチメカニズムを提供します。これらのライブラリは、しばしば次のような機能を提供します:
- ローダー:ルートがレンダリングされる*前*に実行される関数で、データをフェッチしてコンポーネントに渡すことができます。
- アクション:フォーム送信やデータミューテーションを処理する関数。
このようなライブラリを使用すると、特に複雑なアプリケーションにおいて、データ読み込みを大幅に簡素化し、パフォーマンスを向上させることができます。
サーバーサイドレンダリング(SSR)と静的サイト生成(SSG)
SEOと初期読み込みパフォーマンスを向上させるには、Next.jsやGatsbyのようなフレームワークでSSRまたはSSGを使用することを検討してください。これらのフレームワークを使用すると、サーバー上またはビルド時にデータをフェッチし、事前にレンダリングされたHTMLをクライアントに提供できます。これにより、クライアントが初期読み込み時にデータをフェッチする必要がなくなり、より高速でSEOに優しいエクスペリエンスが実現します。
7. 異なるルータータイプの操作
React Router v6は、さまざまな環境やユースケースに合わせて、異なるルーター実装を提供しています:
- BrowserRouter: HTML5 history API(
pushState
、replaceState
)をナビゲーションに使用します。ブラウザ環境で実行されるウェブアプリケーションで最も一般的な選択肢です。 - HashRouter: URLのハッシュ部分(
#
)をナビゲーションに使用します。これは、古いブラウザをサポートする必要があるアプリケーションや、HTML5 history APIをサポートしていないサーバーにデプロイされるアプリケーションに便利です。 - MemoryRouter: 「URL」の履歴をメモリ内(URLの配列)に保持します。React Nativeやテストのような環境で役立ちます。
アプリケーションの要件と環境に最も適したルータータイプを選択してください。
結論
React Router v6は、Reactアプリケーション向けの包括的で柔軟なルーティングソリューションを提供します。このブログ記事で説明したナビゲーションパターンを理解し、適用することで、堅牢で、ユーザーフレンドリーで、保守しやすいウェブアプリケーションを構築できます。<Routes>
と<Route>
による宣言的ルーティングから、URLパラメータを使用した動的ルート、useNavigate
によるプログラムナビゲーション、そして効果的なデータ読み込み戦略まで、React Router v6はユーザーにシームレスなナビゲーション体験を創造する力を与えます。さらなる制御とパフォーマンス最適化のために、高度なルーティングライブラリやSSR/SSGフレームワークの探求を検討してください。これらのパターンを特定のアプリケーション要件に合わせて適応させ、常に明確で直感的なユーザーエクスペリエンスを優先することを忘れないでください。