react-router-domのSwitchは必要？

react-router-domのSwitchコンポーネントは必要なんでしょうか。別になくとも動くんですが、という疑問から書き始めた文章です。

## Switchコンポーネントがなくても動く

この場合も普通に動作します。

```jsx
import React from 'react'
import { BrowserRouter, Route, Link } from 'react-router-dom'
import Home from './Home'
import About from './About'
import Dashboard from './Dashboard'

const App = () => {
  return (
    <BrowserRouter>
      <Route exact path="/">
        <Home />
      </Route>
      <Route path="/about">
        <About />
      </Route>
      <Route path="/dashboard">
        <Dashboard />
      </Route>
      <Link to="/">Back To Home</Link>
    </BrowserRouter>
  )
}
```

## 問題が起こりうる状況

問題が発生するのは以下のような場合です。

userのidとかをpath parameterとして渡したい時このようなコードを書くと思います。この場合 `<About/>` と`<User/>` はどちらも`/about` で表示されてしまいます。

```jsx
import React from 'react'
import { BrowserRouter, Route, Link } from 'react-router-dom'
import Home from './Home'
import About from './About'
import User from './User'

const App = () => {
  return (
    <BrowserRouter>
        <Route exact path="/">
          <Home />
        </Route>
        <Route path="/about">
          <About />
        </Route>
        <Route path="/:user" component={User} />
      <Link to="/">Back To Home</Link>
    </BrowserRouter>
  )
}
```

これはSwitchコンポーネントを親要素とすることで防ぐことができます。

```jsx
import React from 'react'
import { BrowserRouter, Route, Link, Switch } from 'react-router-dom'
import Home from './Home'
import About from './About'
import User from './User'

const App = () => {
  return (
    <BrowserRouter>
      <Switch>
        <Route exact path="/">
          <Home />
        </Route>
        <Route path="/about">
          <About />
        </Route>
        <Route path="/:user" component={User} />
      </Switch>
      <Link to="/">Back To Home</Link>
    </BrowserRouter>
  )
}
```

しかし、`<User />` が`<About />` の兄要素として配置されると`/about` でも常に`<User />`が表示されます。

```jsx
import React from 'react'
import { BrowserRouter, Route, Link, Switch } from 'react-router-dom'
import Home from './Home'
import About from './About'
import User from './User'

const App = () => {
  return (
    <BrowserRouter>
      <Switch>
        <Route exact path="/">
          <Home />
        </Route>
        <Route path="/:user" component={User} />
        <Route path="/about">
          <About /> // 表示されない
        </Route>
      </Switch>
      <Link to="/">Back To Home</Link>
    </BrowserRouter>
  )
}
```

## 公式ドキュメントを読む

[React Router: Declarative Routing for React](https://reacttraining.com/react-router/web/api/Switch)

> Now, if we’re at /about, <Switch> will start looking for a matching <Route>. <Route path="/about"/> will match and <Switch> will stop looking for matches and render <About>. Similarly, if we’re at /michael then <User> will render.

まぁ要は最初にマッチしたコンポーネントをレンダリングするようです。

## ちなみに

以下のようなコードだと先に`<User />`がマッチしてしまうため、`<Switch />`を使用しても`<Dashboard />`は表示されません。こんなコードないと思う。

```jsx
import React from 'react'
import { BrowserRouter, Route, Link, Switch } from 'react-router-dom'
import Home from './Home'
import About from './About'
import User from './User'
import Dashboard from './Dashboard';

const App = () => {
  return (
    <BrowserRouter>
      <Switch>
        <Route exact path="/">
          <Home />
        </Route>
        <Route path="/about">
          <About />
        </Route>
        <Route path="/:user" component={User} />
        <Route path="/:id" component={Dashboard} />
      </Switch>
      <Link to="/">Back To Home</Link>
    </BrowserRouter>
  )
}
```

## 使うべきなのか？

`<Switch />`のコードをみる限り、脳死で使えばいいかと問われると素直に頷けない感じがします。仕様から察することができると思いますが、ルーティングのたびに子要素(`<Route />`)のpropsを順番に検証しているためです([この部分](https://github.com/ReactTraining/react-router/blob/master/packages/react-router/modules/Switch.js#L27-L37))。そのため、後に配置された要素ほど単純に時間がかかると考えられます。多分。

なので、大規模であればURLを適切に設計し`<Switch />`を使用しないという選択があってもいいのかなと思います。まぁ本稿の例みたいに極端なのは流石にないだろ。

```jsx
const App = () => {
  return (
    <BrowserRouter>
      <Switch>
        <Route exact path="/">
          <Home />
        </Route>
        <Route path="/about">
          <About />
        </Route>
	// めっちゃたくさんのRouteが配置された場合、末弟のこいつは最後にマッチすることになる。
        <Route path="/:user" component={User} />
      </Switch>
      <Link to="/">Back To Home</Link>
    </BrowserRouter>
  )
}
```

## 結論

WebサービスのURL設計って大事だね。