Skip to content

mindjuice/react-router-redux

 
 

Repository files navigation

react-router-redux

npm version npm downloads build status

Let react-router do all the work

Formerly known as redux-simple-router

Redux is awesome. React Router is cool. The problem is that react-router manages an important piece of your application state: the URL. If you are using redux, you want your app state to fully represent your UI; if you snapshotted the app state, you should be able to load it up later and see the same thing.

react-router does a great job of mapping the current URL to a component tree, and continually does so with any URL changes. This is very useful, but we really want to store this state in redux as well.

The entire state that we are interested in boils down to one thing: the URL. This is an extremely simple library that just puts the URL in redux state and keeps it in sync with any react-router changes. Additionally, you can change the URL via redux and react-router will change accordingly.

npm install react-router-redux

If you want to install the next major version, use react-router-redux@next. Run npm dist-tag ls react-router-redux to see what next is aliased to.

View the CHANGELOG for recent changes.

Read the API docs farther down this page.

Note: We are currently working on some major changes/improvements to the library. React Router's API in 2.0 is significantly improved and obseletes the need for things like action creators and reading location state from the Redux. This library is still critical to enable things like time traveling and persisting state, so we're not going anywhere. But in many cases, you may not need this library and can simply use the provided React Router APIs. Go check them out and drop some technical debt. 😄

Usage

The idea of this library is to use react-router's functionality exactly like its documentation tells you to. You can access all of its APIs in routing components. Additionally, you can use redux like you normally would, with a single app state.

redux (store.routing)  ↔  react-router-redux  ↔  history (history.location)  ↔  react-router

We only store current URL and state, whereas redux-router stores the entire location object from react-router. You can read it, and also change it with an action.

Tutorial

Let's take a look at a simple example.

Note: This example uses react-router's 2.0 API.

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 { syncHistory, routeReducer } from 'react-router-redux'
import reducers from '<project-path>/reducers'

const reducer = combineReducers(Object.assign({}, reducers, {
  routing: routeReducer
}))

// Sync dispatched route actions to the history
const reduxRouterMiddleware = syncHistory(browserHistory)
const createStoreWithMiddleware = applyMiddleware(reduxRouterMiddleware)(createStore)

const store = createStoreWithMiddleware(reducer)

// Required for replaying actions from devtools to work
reduxRouterMiddleware.listenForReplays(store)

ReactDOM.render(
  <Provider store={store}>
    <Router history={browserHistory}>
      <Route path="/" component={App}>
        <Route path="foo" component={Foo}/>
        <Route path="bar" component={Bar}/>
      </Route>
    </Router>
  </Provider>,
  document.getElementById('mount')
)

Now you can read from state.routing.location.pathname to get the URL. It's far more likely that you want to change the URL more often, however. You can use the push action creator that we provide:

import { routeActions } from 'react-router-redux'

function MyComponent({ dispatch }) {
  return <Button onClick={() => dispatch(routeActions.push('/foo'))}/>;
}

This will change the state, which will trigger a change in react-router. Additionally, if you want to respond to the path update action, just handle the UPDATE_LOCATION constant that we provide:

import { UPDATE_LOCATION } from 'react-router-redux'

function update(state, action) {
  switch(action.type) {
  case UPDATE_LOCATION:
    // do something here
  }
}

But how do I access router props in a Container component?

react-router injects route information via a child component's props. This makes accessing them from a simple component easy. When using a react-redux Container to connect simple components to the store state and dispatch you can access these injected route information from the 2nd argument of mapStateToProps as follows:

function mapStateToProps(state, ownProps) {
  return {
    id: ownProps.params.id,
    filter: ownProps.location.query.filter
  };
}

Examples

Examples from the community:

Have an example to add? Send us a PR!

API

syncHistory(history: History) => ReduxMiddleware

Call this to create a middleware that can be applied with Redux's applyMiddleware to allow actions to call history methods. The middleware will look for route actions created by push, replace, etc. and applies them to the history.

ReduxMiddleware.listenForReplays(store: ReduxStore, selectLocationState?: function)

By default, the syncing logic will not respond to replaying of actions, which means it won't work with projects like redux-devtools. Call this function on the middleware object returned from syncHistory and give it the store to listen to, and it will properly work with action replays. Obviously, you would do that after you have created the store and everything else has been set up.

Supply an optional function selectLocationState to customize where to find the location state on your app state. It defaults to state => state.routing.location, so you would install the reducer under the name "routing". Feel free to change this to whatever you like.

ReduxMiddleware.unsubscribe()

Call this on the middleware returned from syncHistory to stop the syncing process set up by listenForReplays.

routeReducer

A reducer function that keeps track of the router state. You must add this reducer to your app reducers when creating the store. It will return a location property in state. If you use combineReducers, it will be nested under wherever property you add it to (state.routing in the example above).

Warning: It is a bad pattern to use react-redux's connect decorator to map the state from this reducer to props on your Route components. This can lead to infinite loops and performance problems. react-router already provides this for you via this.props.location.

UPDATE_LOCATION

An action type that you can listen for in your reducers to be notified of route updates.

routeActions

An object that contains all the actions creators you can use to manipulate history:

  • push(nextLocation: LocationDescriptor)
  • replace(nextLocation: LocationDescriptor)
  • go(n: number)
  • goForward()
  • goBack()

A location descriptor can be a descriptive object (see the link) or a normal URL string. The most common action is to push a new URL via routeActions.push(...). These all directly call the analogous history methods.

About

Ruthlessly simple bindings to keep react-router and redux in sync

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • JavaScript 100.0%