かんたんにクライアントサイドルーティングを試してみる 後編

## はじめに
[前回の記事](https://qiita.com/course_k/items/09483d4189edce4fbf39)の続きです。

ReactRouterの機能について引き続きまとめていきます。

今回はネストされたルート（Nested Routes）について扱います。

今までは付け焼き刃の知識で実装していたんだな、ということが前回の記事を通してわかったので、しっかりした知識を身につけられるようにします。

## 事前準備
[こちら](https://qiita.com/course_k/items/09483d4189edce4fbf39#%E3%81%BE%E3%81%9A%E3%81%AF%E7%92%B0%E5%A2%83%E6%A7%8B%E7%AF%89)の環境構築が完了している前提です。
それに加えて、わかりやすいように見た目を整えておきたいので、`styled-components`をインストールします。

作成したプロジェクトをVS Codeで開いて、ターミナルから以下のコマンドを入力します。
```bash
npm install styled-components
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/1e79ea79-fbc6-b3ab-c480-4c55b45f9643.png)

インストールが完了次第、以降の工程に進みます。

## そもそもネストされたルートとは
URLのパスの構造と、コンポーネントの階層構造を一致させることができる機能です。

ただ、文字だけだと直感的にわかりにくいので、実際に手を動かしながら確認します。

## ネストされたルートを試す

### どのような見た目になるか
先に完成形を確認します。
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/13ec869d-56b5-a22c-8d67-d43c8e3badcf.png'>
このページの構成として
メニュー
 └設定
 　　└通知
のような階層構造になっています。

また、URLを確認すると
`ルートパス/settings/notification`
になっています。

これがネストされたルートです。
URLのパスの階層構造と、ページ（コンポーネント）の階層構造が一致しているので、作りとしてわかりやすいものになっています。

ReactRouterを使ってどのように実装するのかを見ていきます。

### 実際に作ってみる
特にポイントのないコンポーネントについては、冗長になるので以下から確認してください。

<details><summary>その他のコンポーネントを確認する</summary>

```react:Notification.tsx
const Notification = () => {
  return (
    <>
      <h1>通知</h1>
    </>
  );
};

export default Notification;
```

```React:Mail.tsx
const Mail = () => {
  return (
    <>
      <h1>メール</h1>
    </>
  );
};

export default Mail;
```
上記2つは設定ページのコンポーネントに紐づけます。

```react:Customer.tsx
const Customer = () => {
  return (
    <>
      <h1>顧客情報</h1>
    </>
  );
};

export default Customer;
```
これはホームのコンポーネントに紐づけます。

</details>

#### 子コンポーネントの描画を設定
ポイントは2つあります。
まず、親となるコンポーネントにおいて、子コンポーネントを描画するためのコンポーネントを設定することです。

設定ページのコンポーネントを見てみます。
```react:Settings.tsx
import { Link, Outlet } from "react-router-dom";
import styled from "styled-components";
// スタイルの定義
const SettingsLayout = styled.div`
  display: flex;
  flex-flow: column;
`;
const TopNav = styled.nav`
  width: 70vw;
  border-bottom: 1px double gray;
`;
const TopNavUl = styled.ul`
  display: flex;
  list-style-type: none;
  padding: 0px;
`;
const TopNavLi = styled.li`
  margin-right: 10px;
`;

// 設定ページのコンポーネント
const Settings = () => {
  return (
    <SettingsLayout>
      <h1>設定情報</h1>
      <TopNav>
        <TopNavUl>
          <TopNavLi>
            <Link to="notification">通知</Link>
          </TopNavLi>
          <TopNavLi>
            <Link to="mail">メールアドレス</Link>
          </TopNavLi>
        </TopNavUl>
      </TopNav>
      {/* 子コンポーネントを描画する */}
      <Outlet />
    </SettingsLayout>
  );
};

export default Settings;
```
ごちゃごちゃいろいろ書いていますが、大半はスタイルのための定義なのでおいておきます。
大事なのは`Outlet`コンポーネントです。

このコンポーネントは親子関係のある構造の親コンポーネントの方に定義します。
`Outlet`コンポーネントを定義した部分に、子コンポーネントが描画されます。

なお、現時点では親子関係の定義はしていません。
これはまた別のところで実施します。

:::note warn
`Link`コンポーネントは設定されたパスに飛ばすリンクを生成するだけなので、親子関係の定義とは関係ありません。
:::

ホームのページのコンポーネントにも同様に`Outlet`コンポーネントを定義します。
ポイントは同一なので、実際のコードは以下から確認してください。
<details><summary>ホームのコンポーネントを確認する</summary>

```react:Home.tsx
import { Link, Outlet } from "react-router-dom";
import styled from "styled-components";
// スタイルの定義
const HomeLayout = styled.div`
  display: flex;
`;
const SideNav = styled.nav`
  width: 150px;
  height: 100vh;
  border-right: 1px solid gray;
  margin-right: 20px;
`;
const SideNavUl = styled.ul`
  list-style-type: none;
  padding: 0px;
`;
const SideNavLi = styled.li`
  margin-bottom: 10px;
`;

// ホームのページのコンポーネント
const Home = () => {
  return (
    <HomeLayout>
      <SideNav>
        <h1>メニュー</h1>
        <SideNavUl>
          <SideNavLi>
            <Link to="customer">顧客</Link>
          </SideNavLi>
          <SideNavLi>
            <Link to="settings">設定</Link>
          </SideNavLi>
          <SideNavLi>
            <Link to="/">ホームへ戻る</Link>
          </SideNavLi>
        </SideNavUl>
      </SideNav>
      {/* 子コンポーネントを描画する */}
      <Outlet />
    </HomeLayout>
  );
};

export default Home;
```

</details>

#### 親子関係の設定
次に、`main.tsx`を以下のように書き換えます。

```react:main.tsx
import React from "react";
import ReactDOM from "react-dom/client";
import {
  RouteObject,
  RouterProvider,
  createBrowserRouter,
} from "react-router-dom";
import Home from "./Home.tsx";
import Customer from "./Customer.tsx";
import Settings from "./Settings.tsx";
import Notification from "./Notification.tsx";
import Mail from "./Mail.tsx";
const routes: RouteObject[] = [
  {
    path: "/",
    element: <Home />,
    // Homeコンポーネントの子
    children: [
      {
        path: "customer",
        element: <Customer />,
      },
      {
        path: "settings",
        element: <Settings />,
        // Settingsコンポーネントの子
        children: [
          {
            path: "notification",
            element: <Notification />,
          },
          {
            path: "mail",
            element: <Mail />,
          },
        ],
      },
    ],
  },
];
const router = createBrowserRouter(routes);
ReactDOM.createRoot(document.getElementById("root")!).render(
  <React.StrictMode>
    <RouterProvider router={router} />
  </React.StrictMode>
);
```

基本的な作りは[前回の記事](https://qiita.com/course_k/items/09483d4189edce4fbf39#1-%E3%83%AB%E3%83%BC%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0%E7%94%A8%E6%83%85%E5%A0%B1%E3%81%AE%E7%94%9F%E6%88%90)と変わりません。

ルート情報のオブジェクトに新しく`children`というプロパティを追加しています。
これが親子関係の設定になります。

`customer`と`settings`のパスは`/`のパスの`children`に定義されています。
よって、`/customer`、`/settings`にアクセスされたとき、それぞれのコンポーネントを描画するという設定になります。

ここでのコンポーネントの描画位置は`Outlet`コンポーネントを設定した場所です。

ネストを深くすることもできます。
`settings`にはさらに`children`を設定しています。
これにより、`/settings/notification`、`/settings/mail`にアクセスされたとき、それぞれのコンポーネントを描画します。

### 改めて完成形を確認
ホームのページでは右側には何も表示されません。
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/71969066-3a06-694f-6029-72a8f7929903.png'>

「設定」のリンクを押下すると`settings`へ遷移するため、設定ページが表示されます。
しかし、それ以上は何も表示されません。
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/e48a94e9-0440-aa4c-3905-59034b9f2382.png'>

設定ページの「通知」をクリックすると、設定情報のページに、さらに通知ページが表示されます。
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/0dbea650-96a2-d329-93d8-3dc2c02d287a.png'>

このように、`children`プロパティで親子関係を設定し、親コンポーネントに`Outlet`コンポーネントを設定することで、指定のパスにアクセスされたとき子コンポーネントを描画することができます。

## まとめ
ReactRouterの公式ページから重要と思われる2つの機能を取り上げて紹介しました。
なんとなくクライアントサイドルーティングできるものとして使っていましたが、その使い方や機能の豊富さに驚きました。

時間はかかりますが、公式サイトから基礎的な理解を深め、実践に持っていけるように日々意識したいと思いました。