[React講座] ルーティング(React Router)

当ページのリンクには広告が含まれています。
目次

ルーティング(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-dom

BrowserRouter でラップ

// 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 などが使えるようになります

最小構成:RoutesRoute

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/:iduseParams

よくあるのが「一覧 ⇒ 詳細」パターンです。

  • /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 の中で、前に作った TodoFormTodoList を使えば、「1アプリの中で、複数ページを持つSPA」が成立します。

まとめ

  • SPAでも「URLごとに画面を変える」には React Router(react-router-dom) を使う
  • BrowserRouter でアプリ全体をラップし、Routes / Route でパスとコンポーネントを対応付ける
  • <Link to="..."> でSPA的なページ遷移(リロードなし)
  • :id のようなパラメータ付きルートを作り、useParams で取り出すことで詳細画面が作れる
  • 共通レイアウトは親ルート+Outlet(ネストルート)で実現
  • コード側から遷移したいときは useNavigate を使う

<<前へ(API通信とデータ取得)

>>次へ()

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

CAPTCHA


目次