【React】ルーティング

ルーティングについて、こちらの書籍より学んだことをまとめています。

書籍の説明

書籍名: Reactハンズオンラーニング 第2版 ―Webアプリケーション開発のベストプラクティス
著者: Alex Banks 著
出版社: オライリージャパン 発行
発売日: 2021/8/6

以前に、ルーティングについてはreact-router-domについて、こちらに基本的な内容をまとめていますが、少し異なるのと、更に深堀りしています。

基本的なルーティング

ルーティングの基本的な使い方を、実際に以下5つのリンクを作って見ていきます。

  • Company Website
  • About
  • Events
  • Products
  • Contact

ルーティング作成のステップ

  1. ルーティングの関数(コンポーネント)を作成
  2. index.jsのAppコンポーネントにルーティングを設定
  3. 全てコンポーネントの親コンポーネントであるAppに、Routeを設定
  4. コンポーネントにLinkを使ってリンクを設定
  5. 存在しないパスの処理を404ページを作成して設定

ルーティングの関数(コンポーネント)を作成

srcディレクトリにpages.jsファイルを作成し、この中にルーティングの関数を作成します。

export function Home() {
  return (
    <div>
      <h1>[Company Website]</h1>
    </div>
  );
}

export function About() {
  return (
    <div>
      <h1>[About]</h1>
    </div>
  );
}

export function Events() {
  return (
    <div>
      <h1>[Events]</h1>
    </div>
  );
}

export function Products() {
  return (
    <div>
      <h1>[Products]</h1>
    </div>
  );
}

export function Contact() {
  return (
    <div>
      <h1>[Contact]</h1>
    </div>
  );
}

index.jsのAppコンポーネントにルーティングを設定

react-router-domからBrowserRouterをimportし、index.jsのAppコンポーネントにルーティングを指定します。

これで、App配下にルーティングが適用されました。

import { BrowserRouter as Router } from "react-router-dom";

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

全てコンポーネントの親コンポーネントであるAppに、Routeを設定

上記で、Appにルーティングが使用できるようにしたので、Appコンポーネントの中に各リンクのパスを設定します。

import { Routes, Route } from "react-router-dom";
import { Home, About, Events, Products, Contact } from "./pages";

function App() {
  return (
    <div>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/events" element={<Events />} />
        <Route path="/products" element={<Products />} />
        <Route path="/contact" element={<Contact />} />
      </Routes>
    </div>
  );
}

export default App;

コンポーネントにLinkを使ってリンクを設定

それではリンクを設定していきましょう。

一番最初に作成したルーティング関数にLinkを追加します。

まずはHomeから。

Homeに、navタグで囲ってリンクを追加します。

このnavタグは、主要なナビゲーションを表します。

export function Home() {
  return (
    <div>
      <h1>[Company Website]</h1>
      <nav>
        <Link to="about">About</Link>
        <Link to="events">Events</Link>
        <Link to="products">Products</Link>
        <Link to="contact">Contact us</Link>
      </nav>
    </div>
  );
}

これによってできたのがこちら。4つのリンクができ、それぞれ、ちゃんとルートパスに飛んでいますね!

存在しないパスの処理を404ページを作成して設定

404エラーページのルートのパスには*を指定します。

こうすることで、存在しないルートが指定されたときに404コンポーネントが描画されます。

export function Whoops404() {
  return (
    <div>
      <h1>Resource not found</h1>
    </div>
  );
}

Whoops404のimportと、Routeの追加をします。

import { Routes, Route } from "react-router-dom";
import { Home, About, Events, Products, Contact, Whoops404 } from "./pages";

function App() {
  return (
    <div>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/events" element={<Events />} />
        <Route path="/products" element={<Products />} />
        <Route path="/contact" element={<Contact />} />
        <Route path="*" element={<Whoops404 />} />
      </Routes>
    </div>
  );
}

export default App;

※全てのRouteコンポーネントはRoutesの配下に記述しないといけません。

できたのがこちら。

でたらめのpathを入力すると404に飛びました!

しかし、これには少し問題があります。

これだと、ページが存在しなかったことを教えてくれますが、どのページが存在しなかったのかがわかりません。

そこで、これを解決するためにuseLocationフックを使用します。

このuseLocationを使うと、現在のページの情報を取得することができます。

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

export function Whoops404() {
  let location = useLocation();
  return (
    <div>
      <h1>Resource not found at {location.pathname}</h1>
    </div>
  );
}

これで、存在しなかったページを教えてくれるようになりました!

ネストされたルート

以下のような階層のルーティングを作りたいとします。

Home Page

└ About

└ History

HistoryがAboutにネストしていますが、このような場合の記述の仕方を見ていきます。

以下のようにRouteをネストするだけでいいんですね〜。

        <Route path="about" element={<About />}>
          <Route path="history" element={<History />} />
        </Route>

そして、親コンポーネントであるAboutに、「Outlet」と言うコンポーネントを子コンポーネントとして記述します。

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

export function About() {
  return (
    <div>
      <h1>[About]</h1>
      <Outlet />
    </div>
  );
}

Outletの部分には、locationに対応するパスの子コンポーネントが描画されます。

例えば上記の場合、locationがlocalhost:3000/about/historyなので、Historyコンポーネントが描画されます。

リダイレクト(Navigate)

以前に使用されていたけど、現在はパス名が変わったというような場合、正しいルートへリダイレクトされるようにすることができます。

それには、Redirectコンポーネントを使います。

localhost:3000/servicesにアクセスされたときに、localhost:3000/about/servicesにリダイレクトする例を実装します。

function App() {
  return (
    <div>
      <Routes>
        <Route path="/" element={<Home />} />

        <Route path="about" element={<About />}>
          <Route path="services" element={<Services />} />
          <Route path="history" element={<History />} />
          <Route path="Location" element={<Location />} />
        </Route>

        <Route path="events" element={<Events />} />
        <Route path="products" element={<Products />} />
        <Route path="contact" element={<Contact />} />
        <Route path="*" element={<Whoops404 />} />
        <Navigate from="services" to="about/services" />
      </Routes>
    </div>
  );
}

ところがRedirectを使おうとすることこんなエラーが

export ‘Redirect’ (imported as ‘Redirect’) was not found in ‘react-router-dom’

こちらに記載がありました。

https://reactrouter.com/docs/en/v6/upgrading/v5#:~:text=to%3D%22home%22%20push%20/%3E-,//%20to%20this%3A,-%3CNavigate%20to%3D%22about

RedirectをNavigateに変更します。

しかし、さらにConsoleにこんなエラーがでました。

Uncaught Error: [Navigate] is not a <Route> component. All component children of <Routes> must be a <Route> or <React.Fragment>

こちらのエラーを修正します。

Navigateのルートを以下のように変更し、うまくいきました。

<Route path="services" element={<Navigate to="/about/services" />} />

できたのがこちら

/servicesにアクセスすると、/about/servicesにリダイレクトされることが確認できましたね!!!

ルーティングパラメータ

URLで与えられたパラメータをコンポーネント内で参照することもできます。

ユーザーが色を選択すると、色の詳細情報が表示される機能を実装していきます。

URLに含まれるIDを読み取り、指定された色の情報を表示します。

ColorListコンポーネントを表示する「/」パスと
ColorDetailsコンポーネントを表示する「:id」パスを設定します。

export default function App() {
  return (
    <ColorProvider>
      <AddColorForm />
      <Routes>
        <Route path="/" element={<ColorList />} />
        <Route path=":id" element={<ColorDetails />} />
      </Routes>
    </ColorProvider>
  );
}

パラメータの取得ができるフックuseParamsを使う

上記では、ColorDetailsコンポーネントのpathに:idを指定していますが、そのidを取得する方法があります。

useParamsフックを使用します。

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

export function ColorDetails() {
  let params = useParams();
  console.log(params)

  省略

最終のできたコードがこちら

export function ColorDetails() {
  let { id } = useParams();
  let { colors } = useColors();

  let foundColor = colors.find((color) => color.id === id);

  console.log(id);

  return (
    <div>
      <div
        style={{
          backgroundColor: foundColor.color,
          height: 100,
          width: 100,
        }}
      ></div>
      <h1>{foundColor.title}</h1>
      <h1>{foundColor.color}</h1>
    </div>
  );
}

この操作でやっていることはこちらです。

  1. red, blue, greenの3色を追加し、colors配列を作ります。
  2. そのcolorの持つidと、パラメーターのidを照合し、idが一致したオブジェクトを変数foundColorに代入。

こちらの操作の場合、3つあるオブジェクトの内、呼び出しているのはこちらになります。

{id: '7ca828b4-56f1-4b89-82a7-41b2e64aadab', title: 'red', color: '#dd08a5', rating: 0}

これで、パラメータを使用したJSXの表示ができました!

以上、ご覧いただきありがとうございました。

コメント