サルでもわかったreact-router v5【サンプルの読解と実践】

[5分と5秒かけて作成・デプロイしたTypescript + React + Firebase アプリ](https://qiita.com/Ikko_Kojima/items/28c9b913b7d5723411b3)に 、URLで表示を分ける機能：ルーティング機能を付け加えていきます。



アプリのディレクトリ構成はこんな感じ。基本的には`create-react-app`直後と変わりません。

```
.
|-- README.md
|-- build # 以下略
|-- firebase.json
|-- node_modules # 以下略
|-- package.json
|-- public # 以下略
|-- src
|   |-- App.css
|   |-- App.test.tsx
|   |-- App.tsx
|   |-- index.css
|   |-- index.tsx
|   |-- logo.svg
|   |-- react-app-env.d.ts
|   `-- serviceWorker.ts
|-- tsconfig.json
`-- yarn.lock
```

# React Router DOM の導入

```bash
yarn add react-router-dom
yarn add @types/react-router-dom # typescriptプロジェクトではこちらも必要
```

これで`react-router-dom`(v5.0.1) がインストールされました！

# サンプルを読む

まず、サンプルを読んで使い方を勉強していきます。

## 基本的な使用例

[クイックスタート : 基本的なルーティング](https://reacttraining.com/react-router/web/guides/quick-start/example-basic-routing)のコードをそのまま`src/App.js`に張り付けて動作させると・・・

![router.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131737/1875c2c2-9885-b738-dd60-979ce2926311.gif)
お、URLで表示が変わっていますね！

なるほど、コンポーネントを定義して

```tsx
function Index() {
  return <h2>Home</h2>;
}

function About() {
  return <h2>About</h2>;
}

function Users() {
  return <h2>Users</h2>;
}
```



URLとコンポーネントを紐づけて

```tsx
        <Route path="/" exact component={Index} />
        <Route path="/about/" component={About} />
        <Route path="/users/" component={Users} />
```



任意の要素にLINKの役割を持たせるって感じか

```tsx
            <li>
              <Link to="/about/">About</Link>
            </li>
```



こちらはかなり直感的に使うことができそうです。

## 応用的な使用例

[クイックスタート : ネストされたルーティング](https://reacttraining.com/react-router/web/guides/quick-start/example-nested-routing)のコードをそのまま`src/App.js`に張り付けて動作させると・・・

![router-extends.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131737/e9cae1e1-1826-b810-52c9-31ad6a317cd0.gif)


なるほど、`/topics/components`や`/topics/props-v-state`のような場合ですね。

コードはどうなるのか見ていきましょう。



`/topics`に入るとまずは`Topics`コンポーネントにルーティングされるようです

```tsx
function App() {
  return (
    <Router>
      <div>
        <Header />

        <Route exact path="/" component={Home} />
        <Route path="/about" component={About} />
        <Route path="/topics" component={Topics} />
      </div>
    </Router>
  );
}
```



Topics にルーティングされた場合に、引数 match が流れ込んできて、

`match.url` で URLを引き継ぐ(ネストさせる)ことができるように見えます。

```tsx
function Topics({match}:Props) {
  return (
    <div>
      <h2>Topics</h2>

      <ul>
        <li>
          <Link to={`${match.url}/components`}>Components</Link>
        </li>
        <li>
          <Link to={`${match.url}/props-v-state`}>Props v. State</Link>
        </li>
      </ul>
```

`id`という名前の引数で`/topics`以下にネスとしてきた文字列を受け取って、`Topic`コンポーネントに紐づけているように見えます。

ネストしなかった場合は`Please select a topic.`を`h3`で表示させる処理も書かれていますね。

```tsx
      <Route path={`${match.path}/:id`} component={Topic} />
      <Route
        exact
        path={match.path}
        render={() => <h3>Please select a topic.</h3>}
      />
    </div>
  );
}
```



`Topic`コンポーネントは、`Topics`と同様に引数 match をとるコンポーネントのようです。

match は、`id?` を引数にとる`RouteComponentProps`を`extends`しているので、`match.params.id`という呼び出しができるのでしょう。

```tsx
interface Props extends RouteComponentProps<{ id?: string }>{} // TypeScript対応で付け加え

function Topic({match}:Props) {
  return <h3>Requested Param: {match.params.id}</h3>;
}
```



こちらは読解にあまり自信がないですが、何とか使うことはできそうです。

# マネしてみる

自分のアプリでも`react-router-dom`を使ってみます。

現在作成中のe-Sports専用のクラウドファウンディング・サービス「[e-Sports-Funding(仮)](https://e-sports-funding.firebaseapp.com/)」のルーティング構成はこんな感じがいいかな

- `/` : トップページ
- `/create` : プロジェクト作成画面
- `/search` : プロジェクト検索画面
- `/project/:project_id` : プロジェクト詳細画面
- `/funding/:project_id` : プロジェクト出資画面
- `/mypage/` : マイページ

## 基本的な使用例

ネストは無いものとしてルーティング

![handsOn.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131737/b697dc1f-694f-a3f8-b854-95cbcc21b6fc.gif)


それぞれのコンポーネントたちを`src/components`以下に定義して、`App.tsx`でURLとコンポーネントを紐づけるだけなので、これは簡単です。

## 応用的な使用例

- トップページ・プロジェクト作成画面・プロジェクト検索画面・マイページはどこからでも行ける

- プロジェクト詳細画面はプロジェクト検索画面から引数`:project_id`をとった場合に行ける

- プロジェクト出資画面はプロジェクト詳細画面から引数`:project_id`をとった場合に行ける

みたいな、比較的複雑なルーティングを構成してみます。
![handsOn2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131737/4c660ade-1517-6184-7e66-545cb485dab4.gif)
`App.tsx`にURLとコンポーネントを紐づける設定をすべて書いて、コンポーネントではリンクの設置と、引数の受け渡しのみを行うようにしました。



全員の親となるコンポーネントにルーティング設定を並べたほうが、一覧性が高くて良いかもしれませんね。

リダイレクトも試してみました。

```tsx
const App: React.FC = () => {
  return (
    <Router>
      <div>
        {Header()}
        <Route exact path="/" component={Toppage}/>
        <Route path="/create" component={CreateProject}/>
        <Route path="/search" component={SearchProject}/>
        <Route path="/project/:project_id" component={ProjectDetail}/>
        <Route exact path="/project" render={()=>(<Redirect to="/search"/>)}/>
        <Route path="/funding/:project_id" component={Funding}/>
        <Route exact path="/funding" render={()=>(<Redirect to="/search"/>)}/>
        <Route path="/mypage" component={Mypage}/>
      </div>
    </Router>
  );
}
```

# 今後の展望

ルーティングができたので、次はいよいよ画面！

Material UIで見た目を整えていこうかと思います。



その次は状態変化の制御、続いてデータベースの導入になるのかな。

完成が待ち遠しいです。

# 参考ページ

- 最も信用できる1次情報
  - https://reacttraining.com/react-router/web/guides/quick-start
- match の型が分からなかったので参考にした
  - https://itnext.io/typing-react-4-routing-8a67f97930f0