react-routerとreact-router-domの違い

Ubiregi Advent Calendar 2019 2日目です。1日目は日曜でお休みだったのでした。

フロントエンドエンジニアのコジャが担当します。まだ入社して5ヶ月程度でフロントエンドエンジニアとしても5ヶ月程度です。　

何番煎じかわかりませんが、React-Routerの記事を投稿します。生暖かく見守ってください。いじめないで。

# react-routerとreact-router-domの違い

reactのRoutingライブラリはreact-routerとreact-router-domがあります。

「react-routerがv4から改称してreact-router-domになったんだ!」みたいな印象がありますが(me too)。厳密にいうと違います。

react-router でググって一番上にくる記事がいきなり`npm install --save react-router-dom` とかしてるのでややこしいのですが。

## なにはともあれ

READMEを見てみましょう。今はv5です。

react-router-domはDOMバインディングなreact-routerだよ〜って書いてあります。

https://github.com/ReactTraining/react-router/tree/master/packages/react-router

> react-router
> Declarative routing for React.

https://github.com/ReactTraining/react-router/tree/master/packages/react-router-dom

> react-router-dom
> DOM bindings for React Router.

ちなみに[react-router-native](https://github.com/ReactTraining/react-router/tree/master/packages/react-router-native)ってのもあります。


## 使い方を比較

Home,About,Dashboardはあらかじめ用意したコンポーネントです。

### react-router

```jsx
import React from 'react';
import ReactDOM from 'react-dom';
import { Router, Route, Switch } from 'react-router';
import { createBrowserHistory } from 'history';
import { Home, About, Dashboard } from './component';

const App = () => {
    return (
        <Router history={createBrowserHistory()}>
            <div>{document.title}</div>
            <Switch>
                <Route exact path='/'><Home /></Route>
                <Route path='/about'><About /></Route>
                <Route path='/dashboard'><Dashboard /></Route>
            </Switch>
            <a href='/'>Back To Home</a>
        </Router>
    )
}
```


### react-router-dom

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

const App = () => {
    return (
        <BrowserRouter>
            <div>{document.title}</div>
            <Switch>
                <Route exact path='/'><Home /></Route>
                <Route path='/about'><About /></Route>
                <Route path='/dashboard'><Dashboard /></Route>
            </Switch>
            <Link to='/'>Back To Home</Link>
        </BrowserRouter>
    )
}
```

パッと見、違いとしては以下の感じです。
1. react-routerは`<Router />`に[history](https://github.com/ReactTraining/history)を渡している
2. react-routerはaタグだが、react-router-domは `<Link/>` を使用している。


2に関しては[<Link/>](https://github.com/ReactTraining/react-router/blob/master/packages/react-router-dom/docs/api/Link.md)のAPIをみた方が早い。
1について調べていきます。

## RouterとBrowserRouterの違い

結論から書きますと、この二つにあまり違いはありませんでした。

実装を見てみます。

[react-router/Router](https://github.com/ReactTraining/react-router/blob/master/packages/react-router/modules/Router.js)
[react-router-dom/BrowserRouter](https://github.com/ReactTraining/react-router/blob/master/packages/react-router-dom/modules/BrowserRouter.js)


見た通りですが、BrowserRouterは内部でRouterを使用しており、historyをpropsに渡しています。単なるWrapperですね。

```jsx
class BrowserRouter extends React.Component {
  history = createHistory(this.props);

  render() {
    return <Router history={this.history} children={this.props.children} />;
  }
}
```

PropsTypesではいくつかoptionalなパラメータが定義されていますが、これらのpropsは全てhistoryへ渡されるので、Routerでも`createBrowserHistory`にパラメータを渡すことで同じようなことが実現できます。

```jsx
BrowserRouter.propTypes = {
  basename: PropTypes.string,
  children: PropTypes.node,
  forceRefresh: PropTypes.bool,
  getUserConfirmation: PropTypes.func,
  keyLength: PropTypes.number
};
```

以下はbasenameとforceRefreshを指定した例

```jsx
const App = () => {
    return (
        <Router history={createBrowserHistory({ basename: '/', forceRefresh: true })}>
            <div>{document.title}</div>
            <Switch>
                <Route exact path='/'><Home /></Route>
                <Route path='/about'><About /></Route>
                <Route path='/dashboard'><Dashboard /></Route>
            </Switch>
            <a href='/'>Back To Home</a>
        </Router>
    )
}
```

## あんまり違わないけど...

react-routerもreact-router-domも「Routingをする」だけならば同じように使えます。`useHistory, userLocation, useParams, useRouteMatch` といったhooksもreact-routerに実装があります。

ただ、当然のことをいうと、react-routerだけを使用してもあまり嬉しくありません、`<Link />`や`<NavLink />`といったAPIが使用できませんしね。

## `<Router />`の活用...?

最後に`<Router />`の活用を考えてみましょう。前述したようにBrowserRouterはRouterのWrapperなので、`<Link />`や`<NavLink />`といったAPIも同じように使えます。
historyを別でinstallしなければなりませんが、まぁいいでしょう。

RouterとBrowserRouterの違いはただ一つ、*historyを外部から渡すことができる。* これだけです。
なので、ReactGAと組み合わせてトラッキングが...とか思いましたが、調べたらすでにやってる方がいらっしゃいました。更に言えばuseEffect使う形に落ち着いてた。

https://github.com/react-ga/react-ga/issues/122


更に更に言えば`useLocation`を使った例が公式にありました。

https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/api/hooks.md#uselocation

## 結論

迷わずreact-router-domを使おう。

## 蛇足

ぼんやりとreact-routerのissueを眺めてたらこんなdiscussionがされていました。

[react-router-dom -> react-router/dom #6755](https://github.com/ReactTraining/react-router/issues/6755)


react-routerに依存している別のパッケージがあった時に、バージョンが不一致だと競合するからreact-router-domをreact-router配下のパッケージにしようよーみたいな感じですね。
react-router-nativeに全くメリットがないのと、そもそもreact-routerは直接依存するものではない？ようなので、積極的ではないのかなと思いますが。