React Router (基礎編)
今日はReact Routerの復習。一つずつ理解しながらやっていこう。
React Routerとは
好きな企業のウェブサイトを思い出してほしい。会社概要とか採用情報とか製品情報とか、たくさんのメニューがあって、見たい情報のところをクリックすると別のページに飛ぶ仕組みになっている。URLも後ろの方が変わっていく。
プログラミングの勉強を始めたばかりの頃は <a href=”〜”> を使ってリンクを貼ったりしながら、ページの数だけhtmlファイルを作っていた。
一方でReactはSingle Page Application (SPA)である。言い換えると、Reactのプロジェクトにはhtmlファイルはindex.htmlしかない。このindex.htmlに表示する中身の部分(コンポーネント)はreactが”root”のdivへ渡す内容を取っ替え引っ替え(レンダリング)することで更新していっている。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Platypus Code</title>
<link rel="icon" href="/favicon.ico" />
<link rel="stylesheet" href="src/index.css" />
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
つまり、見た目的にはクリックして新しいページの内容を表示するということができるのだが、実はindex.htmlから動いていないので内容に変化をつけられてもURLがいつまでたっても変わらないということになってしまう。そこで、index.htmlファイルしかなくても”URLも変化するページ移動”をするためにReact Routerを使う。React RouterにはURLに渡したいドメイン以下の部分と、それぞれに該当するコンポーネントなどをセットで設定する。以下に詳しく説明する。
React Routerのインストールとセットアップ
まずはこのコマンドでreact-router-domをインストールする。
npm install react-router-dom
※公式のTutorialでは以下のコマンドが紹介されているがここでは必要ないので無視する。
npm install react-router-dom localforage match-sorter sort-by
main.tsxのファイルを開く。前回の続きからなので今この状態↓
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.tsx";
import "./index.css";
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<App />
</React.StrictMode>
);
これを以下のように書き換える。
import React from "react";
import ReactDOM from "react-dom/client";
import { RouterProvider, createBrowserRouter } from "react-router-dom";
import App from "./App.tsx";
import "./index.css";
const router = createBrowserRouter([
{
path: "/",
element: <App />
}
]);
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<RouterProvider router={router} />
</React.StrictMode>
);
Routesを管理するフォルダを作る
もしページがたくさんあるアプリになりそうであれば、このrouter は別の場所に移しておいた方が良さそう。というわけでsrcのフォルダ下にRoutesというフォルダを作って、その中にRoutes.tsxというファイルを作成。

main.tsxから先ほど書いたばかりの”router”を移動して頭にexportをつける。
import { createBrowserRouter } from "react-router-dom";
import App from "../App";
export const router = createBrowserRouter([
{
path: "/",
element: <App />
}
]);
main.tsxではrouterをインポートする。
import React from "react";
import ReactDOM from "react-dom/client";
import { RouterProvider } from "react-router-dom";
import { router } from "./Routes/Routes.tsx";
import "./index.css";
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<RouterProvider router={router} />
</React.StrictMode>
);
ページを管理するフォルダを作る
こちらもお好みで。ページがたくさんあるアプリになりそうであれば、srcのフォルダ下にページ用のフォルダを作るといいみたい。とりあえず4つのページを作る予定にしてページごとにさらにフォルダ作ってみた。

そのうちの1つ、”HomePage”フォルダ内にHomePage.tsxというファイルを作る。
const HomePage = function () {
return <div className="text-7xl m-20">Home Page</div>;
};
export default HomePage;
他のページも同様にページを作って、Routes.tsxの方で、Appのchildrenにパスとコンポーネントをセットで渡す。このときのpathがURLのドメイン以降に入る値になる。
import { createBrowserRouter } from "react-router-dom";
import App from "../App";
import AboutPage from "../Pages/AboutPage/AboutPage";
import HomePage from "../Pages/HomePage/HomePage";
import PortfolioPage from "../Pages/PortfolioPage/PortfolioPage";
export const router = createBrowserRouter([
{
path: "/",
element: <App />,
children: [
{ path: "", element: <HomePage /> },
{ path: "about", element: <AboutPage /> },
{ path: "portfolio", element: <PortfolioPage /> }
]
}
]);
React Routerではルートの設定方法が大きく2種類ある。今回は公式ページのtutorialを参考にしながら自分が理解しやすかった方法(Objectの形)でやってみる。バージョンによってだいぶ違うようなので、基本的に公式ページを参照する。(ちなみに今回はVersion 6 “react-router-dom”: “^6.21.1″)
Errorページを作る
Errorページは丸パクリで済まそう!と思って、公式ページのコードそのままコピペしようとしたら、’error’ is of type ‘unknown’.っていうタイプエラー出た。なんでよ💦

Route Errorなのか、そうじゃないErrorなのかを場合分けする必要があるらしい。Route Errorなら”error.statusText”、そうじゃないErrorなら”error.message”にエラーの説明が入ってくる。(参照)
import { isRouteErrorResponse, useRouteError } from "react-router-dom";
const ErrorPage = function () {
const error = useRouteError();
let errorMessage: string = "";
if (isRouteErrorResponse(error)) {
errorMessage = error.statusText;
} else if (error instanceof Error) {
errorMessage = error.message;
}
console.error(error);
return (
<div id="error-page">
<h1>Oops!</h1>
<p>Sorry, an unexpected error has occurred.</p>
<p>
<i>{errorMessage}</i>
</p>
</div>
);
};
export default ErrorPage;
errorElementを設定する
Routes.tsxに戻ってerrorElementにErrorページを設定する。
import { createBrowserRouter } from "react-router-dom";
import App from "../App";
import AboutPage from "../Pages/AboutPage/AboutPage";
import HomePage from "../Pages/HomePage/HomePage";
import PortfolioPage from "../Pages/PortfolioPage/PortfolioPage";
import ErrorPage from "../Pages/ErrorPage/ErrorPage";
export const router = createBrowserRouter([
{
path: "/",
element: <App />,
errorElement: <ErrorPage />,
children: [
{ path: "", element: <HomePage /> },
{ path: "about", element: <AboutPage /> },
{ path: "portfolio", element: <PortfolioPage /> }
]
}
]);
localhostでスラッシュの後に”abc”などRoutes.tsxに存在しないページのURL(”abc”)を打ち込んでみると、先ほどのErrorページが表示される。

固定で表示したい部分と変化する部分
現在<App /> の下に3つのchildrenページをぶら下げている。その状態でページ共通のレイアウト部分を<App />に残し、ページの切り替えと合わせて内容を変化させたい部分に<Outlet /> を置いておくと、このOutlet部分にchildrenにぶら下がっているコンポーネントが埋め込まれる。
今回はヘッダーとフッターは常に表示し、Mainの部分をページによって変えていくことにする。

ヘッダーの中にメニューバーを作り、各ページへのリンクは<a href=”〜”>ではなく、<Link to=””> </Link>を使う。
<nav>
<ul className="flex flex-row gap-10 justify-center">
<li className="p-3">
<Link to="">Home</Link>
</li>
<li className="p-3">
<Link to="portfolio">Portfolio</Link>
</li>
<li className="p-3">
<Link to="about">About</Link>
</li>
</ul>
</nav>
そうすると、以下のような感じで、ページが切り替わる。URLにも注目。
うまく切り替わってる!今日はここまで。

SPY FAMILY 好き❤️