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 はリダイレクト処理くらいしかやってはいけないのだと思います。