react-router-redux を 4.0.0 にしたらメソッド名とか中の処理とかけっこう変わってました。
前に書いたURLのフック処理がいろいろ駄目になっていたので、4.0.0でどんな処理になっているのか、ざっと追って、新しくコードを書き直してみました。
react-router-redux が何をやっているのか
最新の 4.0.0 では react-router-redux がラッピングした history オブジェクトを React Router に渡すようになっています。
このラッピングされた history は listen イベント登録が書き換えられており、store に格納されているURLが変更されると、その情報をパラメータに listener がトリガーされるようになっています。
流れを3ステップで説明すると以下のようになります。
- オリジナルの history のURL変更イベントで LOCATION_CHANGE アクションを dispatch する
- routerReducer が LOCATION_CHANGE を受けとって、新しい URL 情報を store に格納する
- react-router のURL監視用 listener が呼び出され、ルーティングが書き換わる
3.0.0からの違い
以下のコードは react-router-redux の README.md にあるもの。
import React from 'react' import ReactDOM from 'react-dom' import { createStore, combineReducers, applyMiddleware } from 'redux' import { Provider } from 'react-redux' import { Router, Route, browserHistory } from 'react-router' import { syncHistoryWithStore, routerReducer } from 'react-router-redux' import reducers from '<project-path>/reducers' // Add the reducer to your store on the `routing` key const store = createStore( combineReducers({ ...reducers, routing: routerReducer }) ) // Create an enhanced history that syncs navigation events with the store const history = syncHistoryWithStore(browserHistory, store) ReactDOM.render( <Provider store={store}> { /* Tell the Router to use our enhanced history */ } <Router history={history}> <Route path="/" component={App}> <Route path="foo" component={Foo}/> <Route path="bar" component={Bar}/> </Route> </Router> </Provider>, document.getElementById('mount') )
以前は syncHistory で middleware が返ってきましたが、4.0.0 の syncHistoryWithStore は react-router-redux がラッピングした history を返してくるので注意が必要。
これを使わずにオリジナルの history を react-router に渡してしまうと、細かいところで変なことが起こります。要注意。
前のバージョンで syncHistory が返していた middleware は、routerMiddleware として別物になってオプション扱い。
というのも、この middleware は URL 変更のアクションを受けとって history に対して操作を行うためにあるのですが、そんな回りくどいことをせずに history を直接呼べば middleware 登録をする必要はないでしょうという話だと思います。
以下のコードは README.md に載っているもので、Redux のアクションを使ってURL遷移をするサンプルコードです。
import { routerMiddleware, push } from 'react-router-redux' // Apply the middleware to the store const middleware = routerMiddleware(browserHistory) const store = createStore( reducers, applyMiddleware(middleware) ) // Dispatch from anywhere like normal. store.dispatch(push('/foo'))
しかし、history を直接使うならば以下のようにシンプルにできます。
import { browserHistory } from 'react-router' browserHistory.push('/foo')
あと、前のバージョンでは index.js に全てがずらずらっと(とは言っても短いですが)書かれていたのが、4.0.0 ではファイルが細切れになっています。
ページ遷移のフック処理
現状、ページ遷移をフックしてAPIを呼び出す処理は以下のようになっています。
import { createStore, applyMiddleware } from 'redux'; import thunkMiddleware from 'redux-thunk'; import createLogger from 'redux-logger'; import { match } from 'react-router'; import { UPDATE_LOCATION } from 'react-router-redux'; import { loading, updateData } from 'actions/common'; import reducer from 'reducers'; const dataFetchMiddleware = routes => store => next => { // Utility method for API call function updateDataFromAPI(location) { match({ routes, location }, (error, redirectLocation, renderProps) => { if (error || redirectLocation) { return; } // Find API const route = renderProps.routes[renderProps.routes.length - 1]; if (route.fetchAPI) { // Call API if it's available api(renderProps.params, renderProps.location.query, data => { store.dispatch(updateData(data)); store.dispatch(loading(false)); }); } else { Promise.resolve(loading(false)).then(store.dispatch); } }); } // Return core middleware function return action => { // Fetch data on update location if (action.type === LOCATION_CHANGE) { next(loading(true)); updateDataFromAPI(action.payload); } return next(action); }; } export default function configureStore(initialState, routes) { return createStore(reducer, initialState, applyMiddleware(thunkMiddleware, loggerMiddleware, dataFetchMiddleware(routes))); }
URLとAPIのマッピングは routes の定義に書くことにしました。
match というユーティリティが react-router にあり、routes 定義と location を渡すと、マッチしたルーティング情報を返してくれます。
末端の route 定義に fetchAPI というキーでデータ取得のための関数を定義するようにして、それをここで取得するようにしました。
注意点は、match が onEnter を呼び出しますので、onEnter で副作用のあることをしないように。そもそも、onEnter はリダイレクト処理くらいしかやってはいけないのだと思います。