React チュートリアル【ルーティング(React Router)】
#React

ルーティング(React Router)


./App.jsx

import { BrowserRouter, Link } from "react-router-dom";

import { Router } from "./router/Router";

import "./styles.css";

export default function App() {
  return (

    <!-- BrowserRouterコンポーネントで囲った範囲でルーティングが有効になる -->
    <BrowserRouter>
      <div className="App">
        <!-- Linkコンポーネント(HTMLでいうaタグの役割)を使用することで簡単にページ遷移の処理を行うことができる Propsのtoに遷移したいURLのパスを指定する-->
        <Link to="/">Home</Link>
        <br />
        <Link to="/page1">Page1</Link>
        <br />
        <Link to="/page2">Page2</Link>
      </div>
      <Router />
    </BrowserRouter>
  );
}


./router/Router.jsx

import { Switch, Route } from "react-router-dom";

import { Home } from "../Home";
import { Page404 } from "../Page404";
import { Page1Routes } from "./Page1Routes";
import { Page2Routes } from "./Page2Routes";

export const Router = () => {
  return (
    <!-- パス毎に呼び出すコンポーネントを出し分ける場合はSwitchコンポーネントで囲った中でRouteコンポーネントを使用する -->
    <Switch>
      <!-- Propsにexcatを渡すことで、pathに完全一致したものを対象にすることができる -->
      <Route exact path="/">
        <Home />
      </Route>
      <Route
        path="/page1"
        <!--RouteコンポーネントにPropsにrenderを渡すことができるrenderにアロー関数を設定しその中でコンポーネントを返却するとレンダリングするコンポーネントを指定することができる -->
        <!-- renderはデフォルトでPropsを受け取っている。その中身はルーティングに関する情報を持ったオブジェクトである。プロパティとしてhistory/location/matchのオブジェクトとstaticContextを持っている。例えば、renderオブジェクトの中のmatchオブジェクトのurlプロパティを参照するとURLの相対パスを受け取ることができる -->
        render={({ match: { url } }) => (

          <!-- Switchは入れ子にできる。この場合、子のRouteコンポーネントpathを親のRouteコンポーネントに指定したpathをルートとした記述とすることができる。 -->
          <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="*"を指定することでどれにも一致しなかった場合のルーティングを設定できる -->
      <Route path="*">
        <Page404 />
      </Route>
    </Switch>
  );
};


./router/Page1Routes

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 />
  }
];


./router/Page2Routes

import { Page2 } from "../Page2";

import { UrlParameter } from "../UrlParameter";

export const Page2Routes = [
  {
    path: "/",
    exact: true,
    children: <Page2 />
  },
  {
    path: "/:id", <!-- :の後にURLパラメータ名を指定できる -->
    exact: false,
    children: <UrlParameter />
  }
];


./UrlParameter.jsx

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

export const UrlParameter = () => {

  // useParamsというHooksを使用することで、URLパラメーターを受け取ることができる
  const { id } = useParams();

  // useLocationというHooksを使用することで、searchとしてURLクエリパラメーターを受け取ることができる
  const { search } = useLocation();
  // javascript標準のURLSearchParams関数に引数としてsearchを渡すことでURLクエリパラメーターを便利に扱える
  const query = new URLSearchParams(search);

  return (
    <div>
      <h1>UrlParameterページです</h1>
      <p>パラメーターは {id} です</p>
      <p>クエリパラメーターは {query.get("name")} です</p>
    </div>
  );
};


./Page2.jsx

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

export const Page2 = () => {
  return (
    <div>
      <h1>Page2ページです</h1>
      <Link to="/page2/999">URL Parameter</Link>
      <br />
      <Link to="/page2/999?name=hogehoge">Query Parameter</Link>
    </div>
  );
};


./Page1.jsx

import { Link, useHistory } from "react-router-dom";

export const Page1 = () => {
  const arr = [...Array(100).keys()];
  // useHistoryというHooksを使用することで、javascriptを使用しての画面遷移を簡単に扱うことができる
  const history = useHistory();
  // history.pushを使用することで引数に指定したpathに遷移させることができる
  const onClickDetailA = () => history.push("/page1/detailA");

  return (
    <div>
      <h1>Page1ページです</h1>
      <!-- Linkコンポーネントのtoの中はオブジェクトを渡すこともできる。-->
      <Link to={{ pathname: "/page1/detailA", state: arr }}>DetailA</Link>
      <br />
      <Link to="/page1/detailB">DetailB</Link>
      <br />
      <button onClick={onClickDetailA}>DetailA</button>
    </div>
  );
};


./Page1DetailA.jsx

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

export const Page1DetailA = () => {
  // useLocationでstateとして任意の変数を受けることもできる
  const { state } = useLocation();
  // useHistoryというHooksを使用することで、javascriptを使用しての画面遷移を簡単に扱うことができる
  const history = useHistory();
  // history.goBackを使用することで前画面に遷移させることができる(ブラウザのバックボタンを同様の挙動)
  const onClickBack = () => history.goBack();
  console.log(state);

  return (
    <div>
      <h1>Page1DetailAページです</h1>
      <button onClick={onClickBack}>戻る</button>
    </div>
  );
};


まとめ

BrowserRouterコンポーネントで囲った範囲でルーティングが有効になる。
パス毎に呼び出すコンポーネントを出し分ける場合はSwitchコンポーネントで囲った中でRouteコンポーネントを使用する。
RouteコンポーネントのPropsにexcatを渡すことで、pathに完全一致したものを対象にすることができる。
RouteコンポーネントにPropsにrenderを渡すことができる。renderにアロー関数を設定し、その中でコンポーネントを返却するとレンダリングするコンポーネントを指定することができる。
renderはデフォルトでPropsを受け取っている。その中身はルーティングに関する情報を持ったオブジェクトである。プロパティとしてhistory/location/matchのオブジェクトとstaticContextを持っている。例えば、renderオブジェクトの中のmatchオブジェクトのurlプロパティを参照するとURLの相対パスを受け取ることができる。
Switchは入れ子にできる。この場合、子のRouteコンポーネントpathを親のRouteコンポーネントに指定したpathをルートとした記述とすることができる。
別ファイルに配列として切り出したパス設定を呼び出してループしてルーティングする事もできる。
useParamsというHooksを使用することで、URLパラメーターを受け取ることができる。
useLocationというHooksを使用することで、searchとしてURLクエリパラメーターを受け取ることができる。
useHistoryというHooksを使用することで、javascriptを使用しての画面遷移を簡単に扱うことができる。