問題
react-router-redux
の @@router/LOCATION_CHANGE
イベントはページ遷移の後に実行される。
import { LOCATION_CHANGE } from 'react-router-redux'
export default function *rootSaga() {
while (true) {
const action = yield take(LOCATION_CHANGE)
// update current user using route parameter
}
}
import React, { Component } from 'react'
import { Provider } from 'react-redux'
import { ConnectedRouter } from 'react-router-redux'
import createHistory from 'history/createBrowserHistory'
import Routes from './Routes'
import configure from './redux/store'
const { store, history } = configure()
store.runSaga()
const App = () => (
<Provider store={store}>
<ConnectedRouter history={history}>
<Routes />
</ConnectedRouter>
</Provider>
)
export default App
import React from 'react'
import Timeline from './Timeline'
const Routes = (props) => (
<Switch>
<Route component={Timeline} exact path="/timeline" />
{// ...}
</Switch>
)
export default Routes
import React, { Component } from 'react'
export default class Timeline extends Component {
componentDidMount() {
this.props.fetchTimelineOfCurrentUser()
}
componentDidUpdate() {
//
}
render() {
//
}
}
ここでもし fetchTimelineOfCurrentUser()
がReduxのステートに依存しているとすると,前のルートに基づくステートをパラメータとして使ってデータフェッチが走ってしまうことになる。
新しいルートに基づいてReduxのステートを再構成した上で,それを使ってcomponentDidMount()
を実行させたい。どうすればいいか?
解決策
ステート更新に非同期処理が入ってくる場合は未解決だが,同期処理のみで済む場合はスマートな解決策がある。
rootSaga
の処理は消した上で StoreUpdater
というコンポーネントを作る。 render
メソッドの返り値は常に null
で問題ない。
import React, { Component } from 'react'
export default class StoreUpdater extends Component {
componentDidMount() {
// do something synchronous store update here
}
componentDidUpdate(prevProps, prevState) {
// do something synchronous store update here
}
render() {
return null
}
}
このコンポーネントを Routes
の中に仕込む。ConnectedRouter
は単一のノードしか受け入れないので Fragment
でラップする。
import React, { Fragment } from 'react'
import StoreUpdater from './StoreUpdater'
import Timeline from './Timeline'
const Routes = (props) => (
<Switch>
<Route component={Timeline} exact path="/timeline" />
...
</Switch>
)
const FreshRoutes = (props) => (
<Fragment>
<StoreUpdater {...props} />
<Routes {...props} />
</Fragment>
)
export default Routes
これだけ。 StoreUpdater
のライフサイクルメソッドは必ず Routes
より先に実行されるので,同期処理のみの場合はこれで解決できる。いちいち全ページコンポーネントの componentDidMount()
componentDidUpdate()
に更新処理を書いたりする必要はない。
非同期処理の場合は…どうするんでしょうね?