目次
ルーティング(React Router)
なぜルーティングが必要か?
ここまでで、「1画面の中にコンポーネントを並べて、状態とイベントで「動くUI」を作る」ところまでは来ました。
でも実際のアプリは:
/… トップページ/todos… Todo一覧/todos/123… Todo詳細/settings… 設定画面
SPAでも、「URLごとに別の画面っぽく見せる」必要があります。
これをいい感じにやってくれるのが React Router(react-router-dom) です。
React Router の導入
Vite + React プロジェクトを前提にします。
インストール
プロジェクトフォルダで下記コマンドを実行:
npm install react-router-domBrowserRouter でラップ
// src/main.jsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import { BrowserRouter } from 'react-router-dom'
import App from './App.jsx'
import './index.css'
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>,
)BrowserRouterが「ルータ本体」- この中で
Route/Linkなどが使えるようになります
最小構成:Routes と Route
App.jsx で「path ⇒ コンポーネント」の対応を定義します。
// src/App.jsx
import { Routes, Route, Link } from 'react-router-dom'
function Home() {
return <h2>ホーム</h2>
}
function About() {
return <h2>このサイトについて</h2>
}
export default function App() {
return (
<div>
<h1>React Router の例</h1>
<nav>
<Link to="/">ホーム</Link> |{' '}
<Link to="/about">このサイトについて</Link>
</nav>
<hr />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</div>
)
}ポイント:
<Routes>の中に<Route>を並べるpath="/"に対してelement={<Home />}を表示<Link to="/about">で、ページ遷移(URL変更+画面切り替え)
<a href="..."> ではなく <Link to="..."> を使う理由:
<a>だとページを丸ごと再読み込みしてしまう(SPAじゃなくなる)<Link>はブラウザの履歴やURLは変えつつ、React内で画面だけ差し替える
動的パラメータ:/users/:id と useParams
よくあるのが「一覧 ⇒ 詳細」パターンです。
/users… ユーザー一覧/users/1… id=1のユーザー詳細/users/2… id=2のユーザー詳細
この「id」のような部分は、react-router-dom では 「URLパラメータ」 として扱います。
ルート定義
import { Routes, Route } from 'react-router-dom'
import UserList from './UserList'
import UserDetail from './UserDetail'
export default function App() {
return (
<Routes>
<Route path="/users" element={<UserList />} />
<Route path="/users/:id" element={<UserDetail />} />
</Routes>
)
}:idの部分がパラメータ名
useParams で取り出す
// src/UserDetail.jsx
import { useParams } from 'react-router-dom'
export default function UserDetail() {
const { id } = useParams()
return (
<div>
<h2>ユーザー詳細</h2>
<p>ユーザーID: {id}</p>
</div>
)
}useParams()は{ id: '1' }のようなオブジェクトを返す- 文字列として渡ってくるので、数字が欲しければ
Number(id)する
実務ではこの id を使って API を叩きます。
// イメージ
useEffect(() => {
fetch(`/api/users/${id}`)
}, [id])ネストしたルートと共通レイアウト
SPAでよくある構成:
- 上の方に 共通ヘッダ・サイドバー
- その下に「画面ごとに内容が変わる部分」
これをReact Routerでは ネストしたルート+Outlet で表現します。
レイアウトコンポーネント
// src/Layout.jsx
import { Link, Outlet } from 'react-router-dom'
export default function Layout() {
return (
<div>
<header>
<h1>管理画面</h1>
<nav>
<Link to="/dashboard">ダッシュボード</Link> |{' '}
<Link to="/users">ユーザー</Link> |{' '}
<Link to="/settings">設定</Link>
</nav>
<hr />
</header>
<main>
<Outlet /> {/* 子ルートの中身がここに差し込まれる */}
</main>
</div>
)
}ルート定義でネスト
// src/App.jsx
import { Routes, Route } from 'react-router-dom'
import Layout from './Layout'
import Dashboard from './Dashboard'
import UserList from './UserList'
import Settings from './Settings'
export default function App() {
return (
<Routes>
<Route path="/" element={<Layout />}>
<Route path="dashboard" element={<Dashboard />} />
<Route path="users" element={<UserList />} />
<Route path="settings" element={<Settings />} />
</Route>
</Routes>
)
}解説:
- ルート
/に対して<Layout>を表示 - その子ルートとして
/dashboard/users/settings
<Layout>の中の<Outlet />の部分に、
子ルートのコンポーネント(Dashboardなど)が差し込まれる
これで、
- ヘッダー&ナビゲーションは共通
- メインコンテンツ部分だけURLごとに変わる
という構造が作れます。
404ページ(存在しないURL)の扱い
存在しないURLにアクセスしたときに、「ページが見つかりません」画面を出したい場合は ワイルドカード * ルート を使います。
function NotFound() {
return <h2>404 - ページが見つかりません</h2>
}
export default function App() {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
{/* どのルートにもマッチしない場合 */}
<Route path="*" element={<NotFound />} />
</Routes>
)
}- 上から順にマッチを試し、どれもダメなら
*に落ちるイメージ
コード側から画面遷移:useNavigate
リンククリックではなく、処理の中から画面遷移したいこともあります。
例:
- フォーム送信成功後に、一覧画面へ戻す
- ログイン成功後にマイページへ遷移
このとき使うのが useNavigate です。
import { useNavigate } from 'react-router-dom'
export default function LoginForm() {
const navigate = useNavigate()
async function handleSubmit(e) {
e.preventDefault()
// ログイン処理(省略)
// 成功したら
navigate('/dashboard')
}
return (
<form onSubmit={handleSubmit}>
{/* ユーザー名・パスワードなど */}
<button type="submit">ログイン</button>
</form>
)
}戻る/進む的な動きもできます:
navigate(-1) // 1つ前のページに戻る(ブラウザバック相当)
navigate(1) // 1つ先へ(ほぼ使わない)「マルチページ」アプリの例
ここまでを組み合わせて、「3画面構成のミニアプリ」 をイメージしておくと整理しやすいです。
構成:
/… ホーム(簡単な説明)/todos… Todo一覧+追加フォーム/about… このアプリについて
App.jsx のイメージ:
import { Routes, Route, Link } from 'react-router-dom'
import TodoPage from './TodoPage'
import AboutPage from './AboutPage'
function HomePage() {
return <h2>ホームです</h2>
}
export default function App() {
return (
<div>
<header>
<h1>ミニSPA</h1>
<nav>
<Link to="/">Home</Link> | <Link to="/todos">Todo</Link> |{' '}
<Link to="/about">About</Link>
</nav>
<hr />
</header>
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/todos" element={<TodoPage />} />
<Route path="/about" element={<AboutPage />} />
<Route path="*" element={<h2>404 - Not Found</h2>} />
</Routes>
</div>
)
}この TodoPage の中で、前に作った TodoForm + TodoList を使えば、「1アプリの中で、複数ページを持つSPA」が成立します。
まとめ
- SPAでも「URLごとに画面を変える」には React Router(
react-router-dom) を使う BrowserRouterでアプリ全体をラップし、Routes/Routeでパスとコンポーネントを対応付ける<Link to="...">でSPA的なページ遷移(リロードなし):idのようなパラメータ付きルートを作り、useParamsで取り出すことで詳細画面が作れる- 共通レイアウトは親ルート+
Outlet(ネストルート)で実現 - コード側から遷移したいときは
useNavigateを使う
>>次へ()
コメント