react-router-domのSwitchは必要?

Created: May 06, 2020 2:40 PM
Last Edited: October 18, 2020 7:24 AM

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

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

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

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 で表示されてしまいます。

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コンポーネントを親要素とすることで防ぐことができます。

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 />が表示されます。

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>
  )
}

公式ドキュメントを読む

loading
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 />は表示されません。こんなコードないと思う。

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を順番に検証しているためです(この部分)。そのため、後に配置された要素ほど単純に時間がかかると考えられます。多分。

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

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設計って大事だね。


<-

<<-