react-routerとは
Reactアプリケーションでルーティング(ページ遷移)を実現するためのライブラリです。ルーティングとは、ユーザーが異なるURLにアクセスしたとき、特定のコンポーネントを表示する仕組みのことです。SPAにおいて、React Routerを使うとページ全体を再読み込みすることなく、URLに応じてコンポーネントを切り替えることができます。
基本的な仕組み
BrowserRouter
まず、Router
について簡単に説明します。Router
は、アプリケーション全体にルーティング機能を提供するコンポーネントです。ルートの履歴管理や、URLの変更に基づいてどのコンポーネントを表示するか決定するために必要です。
BrowserRouter
はReact Router
のコンポーネントの1つで、Reactアプリケーションにルーティング機能を提供します。Router
自体には複数の種類がありますが、最も一般的なのがBrowserRouter
です。
主な役割は、アプリケーション全体にルーティングのコンテキストを提供し、アプリが異なるURLにアクセスしたとき対応するコンポーネントを表示することです。 HTMLのWrapper
のようなものだと認識しています。
具体的に、HTML5の history API (戻る、進むなどの操作履歴の管理)を利用してURLを管理し、ページの再読み込みなしにナビゲーションを実現します。
また、<Router>
の中に別の<Router>
レンダリングすることはできません。App.js
を<BrowserRouter>
で囲った場合、それ以上<BrowserRouter>
は不要です(※重要)
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { BrowserRouter } from "react-router-dom";
import App from "./App";
const rootElement = document.getElementById("root");
const root = createRoot(rootElement);
root.render(
<StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</StrictMode>
);
Routes
Routes
は、複数のルートをグループ化するためのコンポーネントです。URLに応じて表示するルート (path
) を決定し、Route
コンポーネントをレンダリングします。
// プロパティの詳細は後ほど記述します
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
Route
Route
は、パスに対してどのコンポーネントを表示するか定義するコンポーネントです。Route
はURLパス(path
)と表示するコンポーネント(element
)を関連付けます。
<Route path="/" element={<Home />} />
基本的な仕組み まとめ
-
Router(BrowserRouter)
でアプリケーション全体のルーティング機能を有効にします。 -
Routes
でルートのグループを作成し、アプリケーション全体のルートを定義します。 -
Route
で個別のパスに対して対応するコンポーネントを紐づけます。
では実際にルーティングしていきましょう。
react-router-domをインストール
codesandboxを使用しています。
$ npm i react-router-dom
以下を作成しました。
./components
- Home.jsx
- Page1.jsx
- Page2.jsx
基本ルーティング
BrowserRouterが基本的なルーティングです。
App.jsx
は以下の記述になります。
import "./styles.css";
import { Routes, Route, Link } from "react-router-dom";
import Home from "./components/Home";
import Page1 from "./components/Page1";
import Page2 from "./components/Page2";
export default function App() {
return (
<div className="App">
<Link to="/">HOME</Link>
<br />
<Link to="page1">PAGE1</Link>
<br />
<Link to="page2">PAGE2</Link>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/page1" element={<Page1 />} />
<Route path="/page2" element={<Page2 />} />
</Routes>
</div>
);
}
// Page1, Page2も同様に作成します
export default function Home() {
return (
<div>
<h1>HOME</h1>
</div>
);
}
Linkコンポーネント , toプロパティ
ページ間のナビゲーションを行うために使用される重要なコンポーネントです。<a>
タグのようにURLにリンクを提供しますが、ページの再読み込みを発生させずにクライアントサイドでのルーティングを実現します。SPAの特性を活かし、スムーズなページ遷移ができるのです。
Linkコンポーネントはto
プロパティを使って移動先のURLを指定します。href
属性に似ていますが、React Router
ではページリロードなしで指定パスに遷移します。
<Link to="/">HOME</Link>
<Link to="page1">PAGE1</Link>
path , element プロパティ
Route
コンポーネントでは、path
とelement
という2つのプロパティが重要な役割を果たします。path
は、URLパス(ルート)を指定するために使用。
もう1つのelement
は、path
に対応するコンポーネントを表示するために使用します。表示したいコンポーネントをJSX形式で渡すのです。通常、HTMLタグやカスタムコンポーネントが設定されます。
<Route path="/about" element={<About />} />
ここまでが基礎的なルーティングです。
他のルーティングも見ていきましょう。
ネストされたページ遷移
Page1を親とし、子ページ Page1DetailsA.jsx と Page1DetailsB.jsx を作成します。
./components
- Home.jsx
- Page1.jsx
- Page1DetailsA
- Page1DetailsB
- Page2.jsx
import { Link } from "react-router-dom";
export default function Page1() {
return (
<div>
<h1>PAGE1</h1>
<Link to="/page1/detailsA">Page1DetailsA</Link>
<br />
<Link to="/page1/detailsB">Page1DetailsB</Link>
</div>
);
}
import { Link } from "react-router-dom";
export default function Page1DetailsA() {
return (
<div>
<h1>Page1DetailsA</h1>
<Link to="/page1/detailsA">Page1DetailsA</Link>
<br />
<Link to="/page1/detailsB">Page1DetailsB</Link>
</div>
);
}
Page1.jsx からコピーして書き換えただけです。 Page1DetailsB.jsx も同じように操作します。
import "./styles.css";
import { Routes, Route, Link } from "react-router-dom";
import Home from "./components/Home";
import Page1 from "./components/Page1";
import Page2 from "./components/Page2";
import Page1DetailsA from "./components/Page1DetailsA"; // 追加
import Page1DetailsB from "./components/Page1DetailsB"; // 追加
export default function App() {
return (
<div className="App">
<Link to="/">HOME</Link>
<br />
<Link to="page1">PAGE1</Link>
<br />
<Link to="page2">PAGE2</Link>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/page1">
<Route index element={<Page1 />} /> {/*index*/}
<Route path="/page1/detailsA" element={<Page1DetailsA />} /> {/*追加*/}
<Route path="/page1/detailsB" element={<Page1DetailsB />} /> {/*追加*/}
</Route>
<Route path="/page2" element={<Page2 />} />
</Routes>
</div>
);
}
App.js の記述を説明します。まず、ネスト化しているコンポーネントを <Route>
で囲う必要があります。
<Route path="/page1">
...
</Route>
Page1のRouteコンポーネントを index
属性に設定します。
<Route index element={<Page1 />} />
indexプロパティ
親ルートにアクセスしたとき、子ルートを指定しない場合に表示される「デフォルトのページ」を定義します。
通常、ルートはpath
プロパティでURLを指定しますが、index
を使う場合はpath
を指定しません。代わりに、その親ルートが呼び出されたときに表示される「初期コンテンツ」として設定されます。
indexの指定は true
, false
で使われることがありますが、実際にはindexを指定するだけで「true」と同じ意味になります。index={true}
と書く必要はなく、単にindex
と書けば、「このルートはインデックスルート」という意味です。
なお、index={false}
は基本書きません。index
ルートにしたくない場合、そのルートにpath
を指定して通常のルートとして扱います。
URLパラメーターを使う
componentsに UrlParameter.jsx を作成します。
import { useParams } from "react-router-dom";
export default function UrlParameter() {
const { id } = useParams();
return (
<div>
<h1>PAGE2</h1>
<p>URL PARAMETER is {id}</p>
</div>
);
}
import { Link } from "react-router-dom";
export default function Page2() {
return (
<div>
<h1>PAGE2</h1>
// パラメータで10を渡す
<Link to="/page2/10">URL Parameter</Link>
</div>
);
}
import "./styles.css";
import { Routes, Route, Link } from "react-router-dom";
import Home from "./components/Home";
import Page1 from "./components/Page1";
import Page2 from "./components/Page2";
import Page1DetailsA from "./components/Page1DetailsA";
import Page1DetailsB from "./components/Page1DetailsB";
import UrlParameter from "./components/UrlParameter"; // 追加
export default function App() {
return (
<div className="App">
<Link to="/">HOME</Link>
<br />
<Link to="page1">PAGE1</Link>
<br />
<Link to="page2">PAGE2</Link>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/page1">
<Route index element={<Page1 />} />
<Route path="/page1/detailsA" element={<Page1DetailsA />} />
<Route path="/page1/detailsB" element={<Page1DetailsB />} />
</Route>
<Route path="/page2">
<Route index element={<Page2 />} /> {/*index*/}
<Route path=":id" element={<UrlParameter />} /> {/*追加*/}
</Route>
</Routes>
</div>
);
}
useParams
URLに含まれる「動的なパラメータ」にアクセスするため使用されます。ユーザーIDなどを含むURL/users/:id
を定義し、そのIDに基づいたユーザー情報を表示したい場合などです。
// const { 値 } と記述する。なんでも可
const { id } = useParams();
// 今回は { id } なので
// コンポーネントで取得する値は :id となる。
// :値 と記述する
<Route path=":id" element={<UrlParameter />} />
クエリパラメータを使う
components内に QueryParameter.jsx を作成します。
import { useSearchParams } from "react-router-dom";
export const QueryParameter = () => {
const [searchParams, setSearchParams] = useSearchParams();
// .getにてクエリパラメータに設定した値(name)を取得する。
const name = searchParams.get("name");
return (
<div>
<h1>QueryParameter</h1>
<p>Query parameter is {name}</p>
</div>
);
};
import { Link } from "react-router-dom";
export const Page2 = () => {
return (
<div>
<h1>PAGE2</h1>
<Link to="/page2/10">UrlParameter</Link>
<br />
<Link to="/page2/query?name=hello">QueryParameter</Link> {/*nameに値を設定*/}
</div>
);
};
// ...略
import { QueryParameter } from "./components/QueryParameter"; // 追加
export default function App() {
return (
<div className="App">
<Link to="/">HOME</Link>
<br />
<Link to="page1">PAGE1</Link>
<br />
<Link to="page2">PAGE2</Link>
<Routes>
<Route path="/" element={<Home />} />
{/*page1*/}
<Route path="/page2">
<Route index element={<Page2 />} />
<Route path=":id" element={<UrlParameter />} />
<Route path="/page2/query" element={<QueryParameter />} /> {/*追加*/}
</Route>
</Routes>
</div>
);
}
useSearchParams
URLのクエリパラメータを取得したり、変更するために使います。
例えば「検索機能」
検索結果の絞り込みやフィルタ条件をURLに含めて保持したいときに便利です。URLに検索条件を含めることで、ページをリロードしても同じ条件で結果が表示されます。
他にも「フィルタリング」
商品一覧pageや記事一覧pageで、カテゴリ・価格帯などのフィルタを設定する際に使います。フィルタ状態をURLに保存することで、ユーザーがリロードしてもそのフィルタ状態を保持できます。
...
// 基礎的な値を取得する
const [searchParams, setSearchParams] = useSearchParams();
// クエリパラメータ "category" の値を取得する
const category = searchParams.get("category");
// クエリパラメータ "page" の値を取得する
const page = searchParams.get("page");
const handleCategoryChange = (newCategory) => {
// クエリパラメータを設定・更新する
setSearchParams({ category: newCategory, page: "1" }); // 新しいカテゴリを設定し、ページ番号をリセット
};
return (
<div>
<h1>Current Category: {category}</h1>
<h2>Current Page: {page}</h2>
<button onClick={() => handleCategoryChange("electronics")}>
Show Electronics
</button>
<button onClick={() => handleCategoryChange("books")}>
Show Books
</button>
</div>
);
}
404ページ(Not Found)
404ページ(Not Found)を作るには、存在しないURLへアクセスされたときに表示されるコンポーネントを定義します。そのため、path
プロパティにワイルドカードである*
を使用します。*
は指定されたルート以外のすべてのURLにマッチするため、存在しないページにアクセスされたときに404ページを表示できます。
import { Routes, Route, Link } from "react-router-dom";
import Home from "./components/Home";
import NotFound from "./NotFound"; // 追加
export default function App() {
return (
<div className="App">
<Link to="/">HOME</Link>
<Routes>
<Route path="/" element={<Home />} />
<Route path="*" element={<NotFound />} /> {/*追加*/}
</Routes>
</div>
);
}
export default function NotFound() {
return (
<div>
<h1>404: Page Not Found</h1>
</div>
);
}