• 最終更新日:

【React】React Router v6の使い方 - URLによって表示を切り替える

【React】React Router v6の使い方 - URLによって表示を切り替える

今回はReact Router v6の基本的な使い方について説明します。URLによって表示を切り替えるときに必要な機能です。

前提条件は以下

  • VSCode上でcreat react appを使ってReact、TypeScriptの環境で行う
  • ESLint(Airbnb)とPrettierを追加して、VSCode上の拡張機能と連動させている

環境は以下です。

  • macOS Catalina v10.15.5
  • Visual Studio Code v1.57.0
  • React v17.0.2
  • TypeScript v4.5.5
  • react-router-dom v6.2.2
  • node.js v16.13.1
この記事の目次

React Router v6の使い方

ReactでURLごとに表示を切り替えるためにはReact Routerを使う必要があります。基本的な開発環境については以下の記事に沿って作成しています。

React Routerをインストール

以下のコマンドからReact Routerと型をインストールします。

npm i --save react-router-dom @types/react-router-dom

React Routerについては以下の公式をどうぞ。

説明するディレクトリ構造

今回は以下のディレクトリ構造で説明します。
ページ数は全部で4ページです。

  • トップページ(URLは"/")
  • page1(URLは"/page1")
  • page1の子ページ(URLは"/page1/page1Child")
  • page1の孫ページ(URLは"/page1/page1Child/page1Grandchild")
|_public/
|  |- index.html
|
|_src/
   |- assets/ (共通で使うCSSや画像をまとめている)
   |   |- css/
   |   |   |- _base.scss
   |   |   |- _custom-property.scss
   |   |   |- styles.scss
   |   |
   |   |- img/
   |
   |- components/(コンポーネントをまとめている)
   |   |- blocks/(ページを構成するコンポーネント)
   |   |
   |   |- layout/(大きいレイアウトを構成するコンポーネント)
   |   |   |- Layout.module.scss
   |   |   |- Layout.tsx
   |   |
   |   |- pages/(全ページを管理する)
   |   |   |- home/(トップページ)
   |   |   |   |- Home.module.scss
   |   |   |   |- Home.tsx
   |   |   |
   |   |   |- page1/(page1ページ)
   |   |   |   |- Page1.module.scss
   |   |   |   |- Page1.tsx
   |   |   |
   |   |   |- page1Child/(page1の子ページ)
   |   |   |   |- Page1Child.module.scss
   |   |   |   |- Page1Child.tsx
   |   |   |
   |   |   |- page1Grandchild/(page1の孫ページ)
   |   |   |   |- Page1Grandchild.module.scss
   |   |   |   |- Page1Grandchild.tsx
   |   |   |
   |   |
   |   |- types/(共通で使う型をまとめている)
   |
   |- App.tsx
   |- index.tsx

基本的な書き方

App.tsxでURLにごとに表示を切り替えるとします。
例としてトップページとpage1ページを切り替える場合です。

import { BrowserRouter, Route, Routes } from 'react-router-dom';
import { Home } from './components/pages/home/Home';
import { Page1 } from './components/pages/page1/Page1';

const App = () => (
  <BrowserRouter>
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="page1" element={<Page1 />} />
    </Routes>
  </BrowserRouter>
);

export default App;

<Route>ではpathにルートパスを指定して、elementではそのルートパスで表示させたいコンポーネント指定します。

つまり、URLが"/"のトップページでは<Home/>が表示され、URLが"/page1"では<Page1/>が表示されるということ。

そして<Route>の部分は<Routes>と<BrowserRouter>で囲われている必要があります

リンクをクリックして表示を切り替える

Homeコンポーネントにリンクを設置するには以下。

import { Link } from 'react-router-dom';

export const Home = () => (
  <>
    <div>これはトップページです</div>
    <nav>
      <ul>
        <li>
          <Link to="/">トップページです</Link>
        </li>
        <li>
          <Link to="page1">Page1ページです</Link>
        </li>
      </ul>
    </nav>
  </>
);

<Link>を使ってtoの部分にパスを指定します。

つまり、toにpage1が指定されているLinkをクリックすると、URLがpage1に切り替わり、<Route>に指定されているpathが"page1"と一致するため、elementにある<Page1/>が表示されるということ。

URLが深い階層の場合

例えばpage1ページに下層ページがある場合について説明します。
URLが"page1/page1Child"のような場合で、page1Childページを表示させる場合です。

  • page1 … 親ページ
  • page1Child … page1の子ページ

App.tsxでは以下のように書きます。

import { BrowserRouter, Route, Routes } from 'react-router-dom';
import { Layout } from './components/layout/Layout';
import { Home } from './components/pages/home/Home';
import { Page1 } from './components/pages/page1/Page1';
import { Page1Children } from './components/pages/page1Child/Page1Child';

const App = () => (
  <BrowserRouter>
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="page1" element={<Layout />}>
        <Route index element={<Page1 />} />
        <Route path="page1Child" element={<Page1Child />} />
      </Route>
    </Routes>
  </BrowserRouter>
);

export default App;

11〜14行目で<Route>がネスト化されているのがわかります。

11行目のpathに指定されたURLにアクセスすると、12行目のindexがある<Route>のelementで指定されたコンポーネントが表示されます。

13行目の<Route>のpathで指定されたURLは、11行目のpathで指定されたURLより下の階層に位置することになります。今回だと"page1/page1Child"のURLでアクセスされたときに<Page1Child/>が表示されることになります。

重要なのは11行目のelementの指定された<Layout/>です
11行目の<Route>で囲まれた2つの<Route>は、この<Layout/>コンポーネントの中にある<Outlet/>が書かれた箇所にelementで指定したコンポーネントが配置されます。

以下は<Layout/>コンポーネントの例です。

import { Outlet } from 'react-router-dom';
import classes from './Layou.module.scss';

export const Layout = () => (
  <div className={classes.block}>
    <Outlet />
  </div>
);

この<Outlet/>の部分にURLが"/page1"のときに<Page1/>コンポーネントが配置されて、URLが"/page1/page1Child"のときは<Page1Child>コンポーネントが配置されることになります。

URLがもっと深い場合は?

URLが"/page1/page1Child/page1GrandChild"の場合で、page1GrandChildページを表示させる場合は以下です。

14〜17行目のようにネストの中にさらにネストする形になります。

import { BrowserRouter, Route, Routes } from 'react-router-dom';
import { Layout } from './components/layout/Layout';
import { Home } from './components/pages/home/Home';
import { Page1 } from './components/pages/page1/Page1';
import { Page1Children } from './components/pages/page1Child/Page1Child';
import { Page1Grandchild } from './components/pages/page1Grandchild/Page1Grandchild';

const App = () => (
  <BrowserRouter>
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="page1" element={<Layout />}>
        <Route index element={<Page1 />} />
        <Route path="page1Child" element={<Layout />}>
          <Route index element={<Page1Child />} />
          <Route path="page1Grandchild" element={<Page1Grandchild />} />
        </Route>
      </Route>
    </Routes>
  </BrowserRouter>
);

export default App;

存在しないURLの場合は404ページを表示

存在しないURLにアクセスされたときに404ページを表示させる場合は以下です。
pathに*(アスタリスク)を指定します。

import { BrowserRouter, Route, Routes } from 'react-router-dom';
import { Home } from './components/pages/home/Home';
import { Page1 } from './components/pages/page1/Page1';
import { NotFound } from './components/pages/notFound/NotFound';

const App = () => (
  <BrowserRouter>
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="page1" element={<Page1 />} />
      <Route path="*" element={<NotFound />} />
    </Routes>
  </BrowserRouter>
);

export default App;

コンポーネントにデータを渡すには?

elementに指定されているコンポーネントにpropsを使ってデータを渡すこともできます。

import { BrowserRouter, Route, Routes } from 'react-router-dom';
import { Home } from './components/pages/home/Home';
import { Page1 } from './components/pages/page1/Page1';

const App = () => (
  <BrowserRouter>
    <Routes>
      <Route path="/" element={<Home testMessage="トップページです" />} />
    </Routes>
  </BrowserRouter>
);

export default App;

8行目で<Home/>コンポーネントに「トップページです」という文字列を渡しています。この文字列を<Home/>コンポーネントで表示させるのは以下です。

import { VFC } from 'react';

type Props = {
  testMessage: string;
};

export const Home: VFC<Props> = (props) => {
  const { testMessage } = props;
  return (
    <div>
      <div>{testMessage}</div>
    </div>
  );
};

レイアウト用にRouteを使う

以下のように単純にレイアウト用として<Route>を使うこともできます。

import { BrowserRouter, Route, Routes } from 'react-router-dom';
import { BaseLayout } from './components/layout/BaseLayout';
import { Layout } from './components/layout/Layout';
import { Home } from './components/pages/home/Home';
import { Page1 } from './components/pages/page1/Page1';
import { Page2 } from './components/pages/page2/Page2';

const App = () => (
  <BrowserRouter>
    <Routes>
      <Route element={<BaseLayout />}>
        <Route path="/" element={<Home />} />
        <Route path="page1" element={<Page1 />} />
      </Route>
      <Route element={<Layout />}>
        <Route path="page2" element={<Page2 />} />
      </Route>
    </Routes>
  </BrowserRouter>
);

export default App;

<Home/>コンポーネントと<Page1/>コンポーネントは、<BaseLayout/>の中の<OutLet/>の部分に表示されます。

<Page2/>コンポーネントは<Layout/>の中の<OutLet/>の部分に表示されます。

さいごに

今回はReact Router v6の基本的な使い方について説明しました。v5に比べてv6は書き方がシンプルになって分かりやすくなった印象ですね。

Reactで複数のページを作成するときに参考にしてみてください。