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だよ〜って書いてあります。
react-router
Declarative routing for React.
react-router-dom
DOM bindings for React Router.
ちなみにreact-router-nativeってのもあります。
使い方を比較
Home,About,Dashboardはあらかじめ用意したコンポーネントです。
react-router
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
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>
)
}
パッと見、違いとしては以下の感じです。
- react-routerは
<Router />
にhistoryを渡している - react-routerはaタグだが、react-router-domは
<Link/>
を使用している。
2に関してはのAPIをみた方が早い。
1について調べていきます。
RouterとBrowserRouterの違い
結論から書きますと、この二つにあまり違いはありませんでした。
実装を見てみます。
react-router/Router
react-router-dom/BrowserRouter
見た通りですが、BrowserRouterは内部でRouterを使用しており、historyをpropsに渡しています。単なるWrapperですね。
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
にパラメータを渡すことで同じようなことが実現できます。
BrowserRouter.propTypes = {
basename: PropTypes.string,
children: PropTypes.node,
forceRefresh: PropTypes.bool,
getUserConfirmation: PropTypes.func,
keyLength: PropTypes.number
};
以下はbasenameとforceRefreshを指定した例
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使う形に落ち着いてた。
更に更に言えばuseLocation
を使った例が公式にありました。
結論
迷わずreact-router-domを使おう。
蛇足
ぼんやりとreact-routerのissueを眺めてたらこんなdiscussionがされていました。
react-router-dom -> react-router/dom #6755
react-routerに依存している別のパッケージがあった時に、バージョンが不一致だと競合するからreact-router-domをreact-router配下のパッケージにしようよーみたいな感じですね。
react-router-nativeに全くメリットがないのと、そもそもreact-routerは直接依存するものではない?ようなので、積極的ではないのかなと思いますが。