LoginSignup
48
32

More than 1 year has passed since last update.

【React】React Routerを使って簡単に画面遷移をしよう

Last updated at Posted at 2021-10-27

Reactで画面遷移をしよう

Reactで画面遷移をすることが多くあるので方法をまとめていきます。
Reactの画面遷移には、React Routerを使うと簡単に作成することができます。
基本的なルーティング設定の書き方と、分割方法などまとめます。

React Routerの導入

create-react-appでプロジェクトを作成した後、はじめにReactRouterのパッケージをインストールします。

$ yarn add react-router-dom

画面遷移するためのページを用意しよう

試しに画面遷移先を作成したいと思います。今回はHomeとPage1〜Page3を用意しました。

/react-basic/src/components/Home.jsx
export const Home = () => {
  return (
    <div>
      <h1>Home</h1>
    </div>
  );
};

/react-basic/src/components/Page1.jsx
export const Page1 = () => {
  return (
    <div>
      <h1>Page1</h1>
    </div>
  );
};

/react-basic/src/components/Page2.jsx
export const Page2 = () => {
  return (
    <div>
      <h1>Page2</h1>
    </div>
  );
};

/react-basic/src/components/Page3.jsx
export const Page3 = () => {
  return (
    <div>
      <h1>Page3</h1>
    </div>
  );
};

/react-basic/src/index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById("root")
);

基本的な画面遷移

App.jsxにインストールしたReact Routerを使ってルーティングの設定を追加します。
react-router-domのインポートを追加し、BrowserRouterLinkSwitchRouteを使えるようにします。
BrowserRouterでルーティングする設定を囲んであげます。
LinkはHTMLでいうとaタグのようなものです。to=""の中にパスを指定してあげます。
SwitchとRouteはルーティングするコンポーネントを指定します。exactをつけると完全一致になります。Homeはexactをつけてあげます。つけないと、/配下のすべてが対象になってしまいます。

/react-basic/src/App.jsx

// react-router-domのインポートを追加
import { BrowserRouter, Link, Switch, Route } from "react-router-dom";

import { Home } from "./components/Home";
import { Page1 } from "./components/Page1";
import { Page2 } from "./components/Page2";
import { Page3 } from "./components/Page3";

function App() {
  return (
    <BrowserRouter>
      <div className="App">
        <Link to="/">Home</Link>
        <br />
        <Link to="/page1">Page1</Link>
        <br />
        <Link to="/page2">Page2</Link>
        <br />
        <Link to="/page3">Page3</Link>
        <br />

        <Switch>
          {/* exactをつけると完全一致になります。Homeはexactをつけてあげます */}
          <Route exact path="/">
            <Home />
          </Route>
          <Route path="/page1">
            <Page1 />
          </Route>
          <Route path="/page2">
            <Page2 />
          </Route>
          <Route path="/page3">
            <Page3 />
          </Route>
        </Switch>
      </div>
    </BrowserRouter>
  );
}

export default App;

簡単に画面遷移させることができるようになりました。

FireShot Capture 088 - React App - localhost.png

ネストされたページ遷移

先程作成したPage1からネストされたページ遷移を作成したいと思います。今回は、Page1DetailAとPage1DetailBのコンポーネントを2つ用意しました。

/react-basic/src/components/Page1DetailA.jsx
export const Page1DetailA = () => {
  return (
    <div>
      <h1>Page1DetailA</h1>
    </div>
  );
};
/react-basic/src/components/Page1DetailB.jsx
export const Page1DetailB = () => {
  return (
    <div>
      <h1>Page1DetailB</h1>
    </div>
  );
};

Page1にimport { Link } from "react-router-dom"を追加し、遷移先のリンクを記載します。

/react-basic/src/components/Page1.jsx
import { Link } from "react-router-dom";

export const Page1 = () => {
  return (
    <div>
      <h1>Page1</h1>
      <Link to="/page1/detailA">DetailA</Link>
      <br />
      <Link to="/page1/detailB">DetailB</Link>
    </div>
  );
};

最後にルーティング設定を追加します。

react
// react-router-domのインポートを追加
import { BrowserRouter, Link, Switch, Route } from "react-router-dom";

import { Home } from "./components/Home";
import { Page1 } from "./components/Page1";
import { Page2 } from "./components/Page2";
import { Page3 } from "./components/Page3";
import { Page1DetailA } from "./components/Page1DetailA";
import { Page1DetailB } from "./components/Page1DetailB";

function App() {
  return (
    <BrowserRouter>
      <div className="App">
        <Link to="/">Home</Link>
        <br />
        <Link to="/page1">Page1</Link>
        <br />
        <Link to="/page2">Page2</Link>
        <br />
        <Link to="/page3">Page3</Link>
        <br />

        <Switch>
          {/* exactをつけると完全一致になります。Homeはexactをつけてあげます */}
          <Route exact path="/">
            <Home />
          </Route>
          <Route
            path="/page1"
            render={() => (
              <Switch>
                <Route exact path="/page1">
                  <Page1 />
                </Route>
                <Route exact path="/page1/detailA">
                  <Page1DetailA />
                </Route>
                <Route exact path="/page1/detailB">
                  <Page1DetailB />
                </Route>
              </Switch>
            )}
          />
          <Route path="/page2">
            <Page2 />
          </Route>
          <Route path="/page3">
            <Page3 />
          </Route>
        </Switch>
      </div>
    </BrowserRouter>
  );
}

export default App;

実行し、確認します。ネストされたページ遷移ができました。

FireShot Capture 089 - React App - localhost.png

page1を複数書くと混乱につながるので、propsで受け取ったurlを使うように変更すると、下記のように変更することができます。

/react-basic/src/App.jsx
// react-router-domのインポートを追加
import { BrowserRouter, Link, Switch, Route } from "react-router-dom";

import { Home } from "./components/Home";
import { Page1 } from "./components/Page1";
import { Page2 } from "./components/Page2";
import { Page3 } from "./components/Page3";
import { Page1DetailA } from "./components/Page1DetailA";
import { Page1DetailB } from "./components/Page1DetailB";

function App() {
  return (
    <BrowserRouter>
      <div className="App">
        <Link to="/">Home</Link>
        <br />
        <Link to="/page1">Page1</Link>
        <br />
        <Link to="/page2">Page2</Link>
        <br />
        <Link to="/page3">Page3</Link>
        <br />

        <Switch>
          {/* exactをつけると完全一致になります。Homeはexactをつけてあげます */}
          <Route exact path="/">
            <Home />
          </Route>
          <Route
            path="/page1"
            render={({ match: { url } }) => ( // propsでurlを受け取る
              <Switch>
                <Route exact path={url}>
                  <Page1 />
                </Route>
                <Route exact path={`${url}/detailA`}>
                  <Page1DetailA />
                </Route>
                <Route exact path={`${url}/detailB`}>
                  <Page1DetailB />
                </Route>
              </Switch>
            )}
          />
          <Route path="/page2">
            <Page2 />
          </Route>
          <Route path="/page3">
            <Page3 />
          </Route>
        </Switch>
      </div>
    </BrowserRouter>
  );
}

export default App;

ルーティング設定の分割

これまでルーティング設定をApp.jsxに書いてきましたが、コードの見通しを良くするためにルーティング設定の分割を行います。
src配下にrouterのフォルダを作成し、Router.jsxのファイルを作成します。これまでApp.jsxに書いていたルーティング設定を移動させます。

/react-basic/src/components/router/Router.jsx
import { Switch, Route } from "react-router-dom";
import { Home } from "../Home";
import { Page1 } from "../Page1";
import { Page1DetailA } from "../Page1DetailA";
import { Page1DetailB } from "../Page1DetailB";
import { Page2 } from "../Page2";
import { Page3 } from "../Page3";

export const Router = () => {
  return (
    <Switch>
      {/* exactをつけると完全一致になります。Homeはexactをつけてあげます */}
      <Route exact path="/">
        <Home />
      </Route>
      <Route
        path="/page1"
        render={(
          { match: { url } } // propsでurlを受け取る
        ) => (
          <Switch>
            <Route exact path={url}>
              <Page1 />
            </Route>
            <Route exact path={`${url}/detailA`}>
              <Page1DetailA />
            </Route>
            <Route exact path={`${url}/detailB`}>
              <Page1DetailB />
            </Route>
          </Switch>
        )}
      />
      <Route path="/page2">
        <Page2 />
      </Route>
      <Route path="/page3">
        <Page3 />
      </Route>
    </Switch>
  );
};

/react-basic/src/App.jsx
// react-router-domのインポートを追加
import { BrowserRouter, Link } from "react-router-dom";
import { Router } from "./components/router/Router";

function App() {
  return (
    <BrowserRouter>
      <div className="App">
        <Link to="/">Home</Link>
        <br />
        <Link to="/page1">Page1</Link>
        <br />
        <Link to="/page2">Page2</Link>
        <br />
        <Link to="/page3">Page3</Link>
        <br />
      </div>
      <Router />
    </BrowserRouter>
  );
}

export default App;

無事分けることができました!
実行して動作することが確認できました。

FireShot Capture 089 - React App - localhost.png

更にページごとに分割してみる

page1をさらに分割してみます。Page1Routes.jsxというファイルを作成し、オブジェクトとしてルーティング設定を記載します。

/react-basic/src/components/router/Page1Routes.jsx
import { Page1 } from "../Page1";
import { Page1DetailA } from "../Page1DetailA";
import { Page1DetailB } from "../Page1DetailB";

export const page1Routes = [
  {
    path: "/",
    exact: true,
    children: <Page1 />,
  },
  {
    path: "/detailA",
    exact: false,
    children: <Page1DetailA />,
  },
  {
    path: "/detailB",
    exact: false,
    children: <Page1DetailB />,
  },
];

/react-basic/src/components/router/Router.jsx
import { Route, Switch } from "react-router-dom";
import { Home } from "../Home";
import { Page2 } from "../Page2";
import { Page3 } from "../Page3";
import { page1Routes } from "./Page1Routes";

export const Router = () => {
  return (
    <Switch>
      {/* exactをつけると完全一致になります。Homeはexactをつけてあげます */}
      <Route exact path="/">
        <Home />
      </Route>
      <Route
        path="/page1"
        render={({ match: { url } }) => (
          <Switch>
            {page1Routes.map((route) => (
              <Route
                key={route.path}
                exact={route.exact}
                path={`${url}${route.path}`}
              >
                {route.children}
              </Route>
            ))}
          </Switch>
        )}
      />
      <Route path="/page2">
        <Page2 />
      </Route>
      <Route path="/page3">
        <Page3 />
      </Route>
    </Switch>
  );
};

無事分けることができました。ここまで分割すると、見通しが良くなりますね。

FireShot Capture 089 - React App - localhost.png

URLパラメーターを扱う

import { useParams } from "react-router-dom";を使うことで、URLパラメーターを使うことができます。
Page2をURLパラメーターを使うように変更していきます。
pathに:idのように:を使うことでパラメーターを扱うことができます。

react:/react-basic/src/components/router/Page2Routes.jsx
import { Page2 } from "../Page2";
import { UrlParameter } from "../UrlParameter";

export const page2Routes = [
  {
    path: "/",
    exact: true,
    children: <Page2 />,
  },
  {
    path: "/:id", //URLパラメーターを使う
    exact: false,
    children: <UrlParameter />,
  },
];

```react:/react-basic/src/components/UrlParameter.jsx
import { useParams } from "react-router-dom";

export const UrlParameter = () => {
  const { id } = useParams(); //URLパラメーターのIDを受け取る
  return (
    <div>
      <h1>UrlParameter</h1>
      <p>パラメーターは{id}です</p>
    </div>
  );
};

Linkでパラメーターの100を渡すように変更します。

/react-basic/src/components/Page2.jsx
import { Link } from "react-router-dom";

export const Page2 = () => {

  return (
    <div>
      <h1>Page2</h1>
      <Link to="/page2/100">URL Parameter</Link>

    </div>
  );
};
/react-basic/src/components/router/Router.jsx
import { Route, Switch } from "react-router-dom";
import { Home } from "../Home";
import { Page3 } from "../Page3";
import { page1Routes } from "./Page1Routes";
import { page2Routes } from "./Page2Routes";

export const Router = () => {
  return (
    <Switch>
      {/* exactをつけると完全一致になります。Homeはexactをつけてあげます */}
      <Route exact path="/">
        <Home />
      </Route>
      <Route
        path="/page1"
        render={({ match: { url } }) => (
          <Switch>
            {page1Routes.map((route) => (
              <Route
                key={route.path}
                exact={route.exact}
                path={`${url}${route.path}`}
              >
                {route.children}
              </Route>
            ))}
          </Switch>
        )}
      />
      <Route
        path="/page2"
        render={({ match: { url } }) => (
          <Switch>
            {page2Routes.map((route) => (
              <Route
                key={route.path}
                exact={route.exact}
                path={`${url}${route.path}`}
              >
                {route.children}
              </Route>
            ))}
          </Switch>
        )}
      />
      <Route path="/page3">
        <Page3 />
      </Route>
    </Switch>
  );
};

パラメーターを受け取ることができるようになりました。

FireShot Capture 090 - React App - localhost.png

クエリパラメータを扱う

import { useLocation } from "react-router-dom";をインポートすることクエリパラメータを扱うことができます。const { search } = useLocation();でクエリパラメータを受け取る事ができます。
では実際に、Page2に書いていきます。

/react-basic/src/components/UrlParameter.jsx
import { useParams, useLocation } from "react-router-dom";

export const UrlParameter = () => {
  const { id } = useParams(); //URLパラメーターのIDを受け取る
  const { search } = useLocation(); // クエリパラメータを受け取る
  const query = new URLSearchParams(search);
  return (
    <div>
      <h1>UrlParameter</h1>
      <p>パラメーターは{id}です</p>
      <p>クエリは{query.get("name")}です</p> 
    </div>
  );
};

queryページへのリンクを追加します。

/react-basic/src/components/Page2.jsx
import { Link } from "react-router-dom";

export const Page2 = () => {
  return (
    <div>
      <h1>Page2</h1>
      <Link to="/page2/100">URL Parameter</Link>
      <br />
      <Link to="/page2/100?name=hoge">Query Parameter</Link>
    </div>
  );
};

ページ遷移する際に状態を渡す

Linkのtoの箇所をオブジェクトで書くことができ、stateに状態を渡すことができます。

/react-basic/src/components/Page1.jsx
import { Link } from "react-router-dom";

export const Page1 = () => {
  const arr = [...Array(100).keys()]; // 受け渡し用のダミーデータ作成

  return (
    <div>
      <h1>Page1</h1>
      <Link to={{ pathname: "/page1/detailA", state: arr }}>DetailA</Link>
      <br />
      <Link to="/page1/detailB">DetailB</Link>
    </div>
  );
};

const { state } = useLocation();で渡された状態を取得することができます。

import { useLocation } from "react-router-dom";

export const Page1DetailA = () => {
  const { state } = useLocation();
  console.log(state);
  return (
    <div>
      <h1>Page1DetailA</h1>
    </div>
  );
};

無事状態が渡されていることが確認できました。

スクリーンショット 2021-10-07 23.15.12.png

useHistoryを使って画面遷移

useHistoryを使うことで、Linkを使わずに画面遷移させることができます。

/react-basic/src/components/Page1.jsx
import { Link, useHistory } from "react-router-dom";

export const Page1 = () => {
  const arr = [...Array(100).keys()]; // 受け渡し用のダミーデータ作成

  const history = useHistory(); // historyを用意する

  const onClickButton = () => {
    history.push("/page1/detailA"); // 画面遷移を書く
  };

  return (
    <div>
      <h1>Page1</h1>
      <Link to={{ pathname: "/page1/detailA", state: arr }}>DetailA</Link>
      <br />
      <Link to="/page1/detailB">DetailB</Link>
      <br />
      <button onClick={onClickButton}>DetailA</button>
    </div>
  );
};

戻るときは、goback()を使います。

/react-basic/src/components/Page1DetailA.jsx
import { useLocation, useHistory } from "react-router-dom";

export const Page1DetailA = () => {
  const { state } = useLocation();

  const history = useHistory(); // historyを用意する

  const onClickButton = () => {
    history.goBack(); // 戻ることができる
  };

  return (
    <div>
      <h1>Page1DetailA</h1>
      <button onClick={onClickButton}>戻る</button>
    </div>
  );
};

404ページを作成してみる

はじめに404ページを作成します。

/react-basic/src/components/UrlParameter.jsx
export const Page404 = () => {
  return (
    <div>
      <h1>Page404</h1>
    </div>
  );
};

ルーティング設定の一番下に、path="*"を追加し、pathに一致しなかった場合すべて404ページが表示されるようします。

/react-basic/src/components/router/Router.jsx
import { Route, Switch } from "react-router-dom";
import { Home } from "../Home";
import { Page3 } from "../Page3";
import { Page404 } from "../Page404";
import { page1Routes } from "./Page1Routes";
import { page2Routes } from "./Page2Routes";

export const Router = () => {
  return (
    <Switch>
      {/* exactをつけると完全一致になります。Homeはexactをつけてあげます */}
      <Route exact path="/">
        <Home />
      </Route>
      <Route
        path="/page1"
        render={({ match: { url } }) => (
          <Switch>
            {page1Routes.map((route) => (
              <Route
                key={route.path}
                exact={route.exact}
                path={`${url}${route.path}`}
              >
                {route.children}
              </Route>
            ))}
          </Switch>
        )}
      />
      <Route
        path="/page2"
        render={({ match: { url } }) => (
          <Switch>
            {page2Routes.map((route) => (
              <Route
                key={route.path}
                exact={route.exact}
                path={`${url}${route.path}`}
              >
                {route.children}
              </Route>
            ))}
          </Switch>
        )}
      />
      <Route path="/page3">
        <Page3 />
      </Route>

      {/* 404ページ追加 */}
      <Route path="*">
        <Page404 />
      </Route>
    </Switch>
  );
};

404ページが表示されるようになりました。

FireShot Capture 093 - React App - localhost.png
48
32
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
48
32