Reactキャッチアップへの道③ 『ルーティング』
こんにちは! スマレジ テックファームのMichiです!
前週に引き続き、Reactのキャッチアップをしていきます。今回は、『Reactのルーティング』について学びます。
はじめに
Reactでルーティングを設定するにあたって、『React Router』というライブラリを使用します。
下記コマンドで、ライブラリをインストールしてください。
npm install --save react-router-dom
※本記事ではReact Routerの最新バージョンである6系で解説を進めます。
また、ディレクトリ構造は以下のようになります。
- root - src - App.js - Home.jsx - Page1.jsx - Page2.jsx - index.js
それでは、はじめていきましょう。
基本的なルーティング
React Routerを使用するには、<BrowserRouter>
を使います。<BrowserRouter>
で囲った範囲でのみルーティングが有効になるので、ルートコンポーネントであるApp.js
の一番外側に<BrowserRouter>
を適用します。
App.js
import { BrowserRouter } from "react-router-dom"; export default function App() { return ( <BrowserRouter> <div className="App"></div> </BrowserRouter> ); }
<Link>
タグを使用することで、ハイパーリンクの要素を作成することができます。要素をクリックすると、to
属性で指定したパスへ遷移します。(HTMLの<a>
タグと同じです。)
import { BrowserRouter, Link } from "react-router-dom"; export default function App() { return ( <BrowserRouter> <div className="App"> <Link to="/">Home</Link> <br /> <Link to="/page1">Page1</Link> <br /> <Link to="/page2">Page2</Link> </div> </BrowserRouter> ); }
次に、パスとコンポーネントの紐づけを行います。
<Routes>
タグを使うと、囲った範囲の中で、指定したパス(URL)に紐づいたコンポーネントの切り替えができます。
<Routes>
配下では、<Route>
タグを使って、パスとコンポーネントの紐づけを行います。path
属性にパス(URL)、element
属性にコンポーネントを指定します。
import { BrowserRouter, Link, Route, Routes } from "react-router-dom"; import { Home } from "./Home"; import { Page1 } from "./Page1"; import { Page2 } from "./Page2"; export default function App() { return ( <BrowserRouter> <div className="App"> <Link to="/">Home</Link> <br /> <Link to="/page1">Page1</Link> <br /> <Link to="/page2">Page2</Link> </div> <Routes> <Route path="/" element={<Home />} /> <Route path="/page1" element={<Page1 />} /> <Route path="/page2" element={<Page2 />} /> </Routes> </BrowserRouter> ); }
これで、各コンポーネントのリンクをクリックしたとき、対象のコンポーネントへ遷移することができるようになります。
Not Foundページの設定
現状、先ほど指定した以外のURLにアクセスすると、各コンポーネントへのリンク以外、画面に何も表示されません。
これではユーザーに混乱を与えてしまいますので、専用の『Not Found』ページを作成します。 まずは、『Not Found』用のコンポーネントの作成からです。
NotFound.jsx
import { Link } from "react-router-dom"; export const NotFound = () => { return ( <div> <h1>ページが見つかりません</h1> <Link to="/">TOPに戻る</Link> </div> ); };
次に、App.js
へNotFound.jsx
へのルーティングを追加します。最初に設定したどのルーティングにも該当しない場合、 『Not Found』ページを出したいので、ルーティングの一番下にpath="*"
で指定した<Route>
タグを配置します。
App.js
import { BrowserRouter, Link, Route, Routes } from "react-router-dom"; import { Home } from "./Home"; import { NotFound } from "./NotFound"; import { Page1 } from "./Page1"; import { Page2 } from "./Page2"; export default function App() { return ( <BrowserRouter> <div className="App"> <Link to="/">Home</Link> <br /> <Link to="/page1">Page1</Link> <br /> <Link to="/page2">Page2</Link> </div> <Routes> <Route path="/" element={<Home />} /> <Route path="/page1" element={<Page1 />} /> <Route path="/page2" element={<Page2 />} /> {/* 追加 */} <Route path="*" element={<NotFound />} /> </Routes> </BrowserRouter> ); }
これで、どのルーティングにも該当しない場合、『Not Found』ページを表示することができました。
ネストされたルーティング
/page1/detail
のようなネストされたルーティングを作成します。まずは、Detail.jsx
コンポーネントを作成します。
Detail.jsx
export const Detail = () => { return ( <div> <h1>Page1のDetailです</h1> </div> ); };
次に、/page1/detail
のルーティングを追加します。
App.js
の/page
へのルーティングを設定している箇所に、ネストしてルーティングを追加します。パスは親ルートからの続きで書くので、path="detail"
とします。(/
がいらないことに注意!)
App.js
import { BrowserRouter, Link, Route, Routes } from "react-router-dom"; import { Detail } from "./Detail"; import { Home } from "./Home"; import { NotFound } from "./NotFound"; import { Page1 } from "./Page1"; import { Page2 } from "./Page2"; export default function App() { return ( <BrowserRouter> <div className="App"> <Link to="/">Home</Link> <br /> <Link to="/page1">Page1</Link> <br /> <Link to="/page2">Page2</Link> </div> <Routes> <Route path="/" element={<Home />} /> {/* ネストされるルーティングを追加 */} <Route path="/page1" element={<Page1 />}> <Route path="detail" element={<Detail />} /> </Route> <Route path="/page2" element={<Page2 />} /> <Route path="*" element={<NotFound />} /> </Routes> </BrowserRouter> ); }
最後に、Page1.jsx
に/page1/detail
へのリンクと、<Outlet>
タグを追加します。<Outlet>
は親ルートのコンポーネントに、その子ルートのコンポーネントをどこへレンダリングするか伝えるための要素です。<Outlet>
がない場合は正しくレンダリングされないので、注意しましょう。
Page1.js
import { Link, Outlet } from "react-router-dom"; //追加 export const Page1 = () => { return ( <div> <h1>Page1です</h1> {/* 追加 */} <Link to="/page1/detail">Detail</Link> <Outlet /> </div> ); };
これで、ネストされたルーティングも追加することができました。
ルーティング定義の切り分け
さて、今までApp.js
ファイルに配下のコンポーネントとルーティングのすべての情報を書いてきましたが、情報量が多くなり、だんだんと見づらくなってきました。
そこで最後に、ルーティングの定義部分だけを別ファイルへ切り出します。src/router
ディレクトリを作成し、その直下にRouter.jsx
ファイルを作成します。
- root - src - router - Router.jsx - App.js - Detail.jsx - Home.jsx - Page1.jsx - Page2.jsx - index.js
次に、App.js
内の<Routes>
配下を丸ごと切り出し、router/Router.jsx
内へ移動させます
Router.jsx
import { Route, Routes } from "react-router-dom"; import { Detail } from "../Detail"; import { Home } from "../Home"; import { NotFound } from "../NotFound"; import { Page1 } from "../Page1"; import { Page2 } from "../Page2"; export const Router = () => { return ( <Routes> <Route path="/" element={<Home />} /> <Route path="/page1" element={<Page1 />}> <Route path="detail" element={<Detail />} /> </Route> <Route path="/page2" element={<Page2 />} /> <Route path="*" element={<NotFound />} /> </Routes> ); };
App.js
側では、切り出したRouter.jsx
をインポートして、元の場所に配置します。
App.js
import { BrowserRouter, Link } from "react-router-dom"; import { Router } from "./router/Router"; export default function App() { return ( <BrowserRouter> <div className="App"> <Link to="/">Home</Link> <br /> <Link to="/page1">Page1</Link <br /> <Link to="/page2">Page2</Link> </div> {/* 切り出したRouterをインポート */} <Router /> </BrowserRouter> ); }
これで、責務分割ができている見やすいコンポーネントになりましたね。
まとめ
Reactキャッチアップの3週目は、React Routerを使ったルーティングの設定方法でした。
ちなみに、React Router 5系から6系へバージョンアップした際、大幅な変更があったようで、この記事で書いた内容は5系では動きませんので、注意してください。