Skip to content

Use react-router-redux@4 #1414

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 17, 2016
Merged

Use react-router-redux@4 #1414

merged 1 commit into from
Feb 17, 2016

Conversation

gaearon
Copy link
Contributor

@gaearon gaearon commented Feb 17, 2016

React Router Redux recently incorporated the changes originally suggested in #1362. The RC of 4.x is out, so I encourage you to start migrating from the old versions.

The 4.x release is motivated by the desire to fix a few long-standing hard-to-fix bugs, become closer to Redux way of doing things, and by API changes in React Router 2.0. I know this is a lot of churn but it comes out of the lessons we learned from the real world usage. After this change, the project should be much more stable.

In case you haven’t been following, here is what changed:

You May Remove the Middleware

Since React Router 2.0 gives us convenient history singletons (hashHistory and browserHistory), the middleware to handle history actions is no longer necessary. It still exists for those who prefer to dispatch(push()) rather than history.push(). In fact it is as straightforward as middleware gets.

Feel free to use it if you still like it. However we encourage you to just use the singletons provided by React Router instead:

-import { syncHistory } from 'react-router-redux'
-import { browserHistory } from 'react-router'
 import thunk from 'redux-thunk'
 import createLogger from 'redux-logger'
 import api from '../middleware/api'
 import rootReducer from '../reducers'
 import DevTools from '../containers/DevTools'

-const reduxRouterMiddleware = syncHistory(browserHistory)

 export default function configureStore(initialState) {
   const store = createStore(
     rootReducer,
     initialState,
     compose(
-      applyMiddleware(thunk, api, reduxRouterMiddleware, createLogger()),
+      applyMiddleware(thunk, api, createLogger()),
       DevTools.instrument()
     )
   )

-  // Required for replaying actions from devtools to work
-  reduxRouterMiddleware.listenForReplays(store)
-
 import React, { Component, PropTypes } from 'react'
 import { connect } from 'react-redux'
-import { push } from 'react-router-redux'
+import { browserHistory } from 'react-router'
 import Explore from '../components/Explore'
 import { resetErrorMessage } from '../actions'

 @@ -17,7 +17,7 @@ class App extends Component {
   handleChange(nextValue) {
-    this.props.push(`/${nextValue}`)
+    browserHistory.push(`/${nextValue}`)
   }

 // ...

 export default connect(mapStateToProps, {
-  resetErrorMessage,
-  push
+  resetErrorMessage
 })(App)

Many people said that they used those action creators so they are logged for analytics. This no longer installing middleware or using those action creators. With 4.x, when you push() to a history, a special action will still be dispatched on every history change, so you can log that instead. And of course you can always browserHistory.listen(location => ...) yourself.

Why would you still want the middleware then? I’ve heard that it can be helpful for some use cases but unless you know you need it, don’t use it.

I removed the usage of middleware in this example.

Prefer to Read the Routing State from Props

This example used to read routing state from state.routing. This was actually a bad idea and something that React Router Redux README had a warning for. If you use asynchronous transitions in React Router, you might get into infinite loops if you read state.routing in components. Why? The location in state.routing describes the location before any transitions—the one obtained from the URL bar. Router is stateful and lets you do async work before showing the related UI, but this is why you should always be reading the location from the props supplied to you by the router.

This is why do this:

-function mapStateToProps(state) {
+function mapStateToProps(state, ownProps) {
   return {
     errorMessage: state.errorMessage,
-    inputValue: state.routing.location.pathname.substring(1)
+    inputValue: ownProps.location.pathname.substring(1)
   }
 }

We still keep the routing state in state.routing (the reducer key is up to you) so you can read it from your custom middleware (for example, for analytics). However you will notice that the actual location is in state.routing.locationBeforeTransitions. This is our way of warning you that this is not the location you want to use in your UI.

Synchronizing with the Store

So why use this library at all then? It only buys you a few things:

  • It keeps the current location as reported by history in your Redux store so you may read it
  • It makes Redux store “the source of truth” for the router, so replay in Redux DevTools “just works”
  • It provides an optional middleware and action creators for those who enjoy them

If you don’t care about these features, just don’t use it, and use React Router 2.0 directly.

If you do care, the last thing you need to change is to wrap your history before passing it to the router. This is how we translate history changes into Redux actions, and make the store a source of truth for the router. You only need to do it once wherever you render the <Router>:

+import { browserHistory } from 'react-router'
+import { syncHistoryWithStore } from 'react-router-redux'
 import Root from './containers/Root'
 import configureStore from './store/configureStore'

 const store = configureStore()
+const history = syncHistoryWithStore(browserHistory, store)

 render(
-  <Root store={store} />,
+  <Root store={store} history={history} />,
   document.getElementById('root')
 )

This is it. See the master branch of React Router Redux for a normal and a server rendering example.

@timdorr
Copy link
Member

timdorr commented Feb 17, 2016

Thanks for the writeup, Dan!

gaearon added a commit that referenced this pull request Feb 17, 2016
@gaearon gaearon merged commit 91dd4a2 into master Feb 17, 2016
@gaearon gaearon deleted the react-router-redux-4 branch February 17, 2016 16:56
@c0
Copy link

c0 commented Feb 17, 2016

💯 Thank you for the diffs! It makes it very easy to migrate.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants