react-router-redux 5.xで正常にロケーションを反映できない場合の対処法
2018-01-15
「react-router-redux 5.x」をサンプルの通りインストールすると、場合によってはブラウザの戻る/進む操作によって画面の状態がおかしくなることがあります。
2018年1月現在、react-router-redux 5.xの最終バージョンがv5.0.0-alpha.9
なので、まだドキュメントが整備されていないだけかもしれませんが、詳しい情報の記載がないため、どう修正すればいいかわからない方も多いかと思いますので、備忘録を兼ね紹介しておきます。
現象
react-router-redux 5.xをインストールした環境において、ブラウザの戻るボタンを連打すると、画面の状態が戻ったり戻らなかったりし、更に進むボタンを押すと、更に進んだり進まなかったりし、結果的に画面の各コンポーネントにおける状態がズレたりします。
例えば、特定の画面を開いたときアクティブな<NavLink/>
に特定のスタイルを当てていたとして、戻る/進むボタンによって、実際に表示されている画面とアクティブな<NavLink/>
とにズレが生じたりします。
実際はトップページを開いているのに、<NavLink/>
は別のページを開いていることになっていたり。
react-router-redux 5.xのインストール手順
2018年1月現在、下記の手順でインストールしたものが5.xのものとなります。それ以前のバージョンは、本記事での解説の対象外です。
npm install --save react-router-redux@next
npm install --save history
現象が発生するコード
GitHubのREADME.mdに記載のサンプルコードで、該当の現象が発生します。
import React from 'react'
import ReactDOM from 'react-dom'
import { createStore, combineReducers, applyMiddleware } from 'redux'
import { Provider } from 'react-redux'
import createHistory from 'history/createBrowserHistory'
import { Route } from 'react-router'
import { ConnectedRouter, routerReducer, routerMiddleware } from 'react-router-redux'
import reducers from './reducers'
const history = createHistory()
const middleware = routerMiddleware(history)
const store = createStore(
combineReducers({
...reducers,
router: routerReducer
}),
applyMiddleware(middleware)
)
ReactDOM.render(
<Provider store={store}>
<ConnectedRouter history={history}>
<div>
<Route exact path="/" component={Home}/>
<Route path="/about" component={About}/>
<Route path="/topics" component={Topics}/>
</div>
</ConnectedRouter>
</Provider>,
document.getElementById('root')
)
対処法
各<Route/>
コンポーネントの親要素として、プロパティlocation
にreact-router-reduxによって生成されたlocation
オブジェクトを割り当てた、<Switch/>
コンポーネントを定義します。
以下のサンプルでは、その<Switch/>
コンポーネントを<ConnectedSwitch/>
として定義しています。
import React from 'react'
import ReactDOM from 'react-dom'
import { createStore, combineReducers, applyMiddleware } from 'redux'
import { Provider } from 'react-redux'
import createHistory from 'history/createBrowserHistory'
import { Route, Switch } from 'react-router'
import { ConnectedRouter, routerReducer, routerMiddleware } from 'react-router-redux'
import reducers from './reducers'
const history = createHistory()
const middleware = routerMiddleware(history)
const store = createStore(
combineReducers({
...reducers,
router: routerReducer
}),
applyMiddleware(middleware)
)
const ConnectedSwitch = connect(state => ({
location: state.router.location
}))(Switch)
ReactDOM.render(
<Provider store={store}>
<ConnectedRouter history={history}>
<ConnectedSwitch>
<Route exact path="/" component={Home}/>
<Route path="/about" component={About}/>
<Route path="/topics" component={Topics}/>
</ConnectedSwitch>
</ConnectedRouter>
</Provider>,
document.getElementById('root')
)
また、<NavLink/>
を利用している場合は、<NavLink/>
に対しても同様に、プロパティlocation
にreact-router-reduxによって生成されたlocation
オブジェクトを割り当てる必要があります。
import { connect } from 'react-redux'
import { NavLink } from 'react-router-dom'
export const ConnectedNavLink = connect(state => ({
location: state.router.location
}))(NavLink)
こうすることで、問題は解決するかと思います。
まとめ
react-router-redux 5.xからreact-routerパッケージに含まれるようになり、本バージョンはreact-router 4.xと互換性を持ちます。
JavaScriptライブラリはどれも他の言語のライブラリに比べ変更が多く、バージョンアップ等の対応に悩まされることも多いかと思いますが、少しでもこの記事がそういった方々の助けになれば幸いです。