Router5 integration with redux. If you develop with React, use this package with react-redux and react-router5. Using router5 with redux removes the need to include router5-listeners.
Example | Demo | Learn router5
- router5 >= 4.0.0
- redux >= 3.0.0
- Create and configure your router instance
- Create and configure your store including
router5Middleware
androuter5Reducer
- If you don't use the middleware, add
reduxPlugin
to your router instance - Use
routeNodeSelector
on route nodes in your component tree - Use provided actions to perform routing or use your router instance directly
Breaking change from 4.x: the middleware doesn't pass the store to your router instance (using .inject()
). If you want to use your store in canActivate
, canDeactivate
, middlewares and plugins, use router.inject(store)
.
import { ReactDOM } from 'react-dom';
import { RouterProvider } from 'react-router5';
import { Provider } from 'react-redux';
import React from 'react';
import App from './app';
import createRouter from './create-router';
import configureStore from './store';
const router = createRouter();
const store = configureStore(router);
router.start(() => {
ReactDOM.render(
(
<Provider store={ store }>
<RouterProvider router={ router }>
<App />
</RouterProvider>
</Provider>
),
document.getElementById('app')
);
});
Note: RouterProvider
comes from react-router5
. It simply adds your router instance in your application context, which is required. Alternatively, you can use withContext()
from recompose. You also may not need it: having your router in context gives you access router methods like buildUrl
, isActive
, etc... If you don't use those methods, then you don't need your router instance in context.
import createRouter from './create-router';
import { compose, createStore, applyMiddleware, combineReducers } from 'redux';
import { router5Middleware, router5Reducer } from 'redux-router5';
function configureStore(router, initialState = {}) {
const createStoreWithMiddleware = applyMiddleware(router5Middleware(router))(createStore);
const store = createStoreWithMiddleware(combineReducers({
router: router5Reducer,
/* your reducers */
}), initialState);
return store;
}
const router = createRouter();
const store = configureStore(router, { router: { route: state }});
router.start();
Under the hood, it simply adds a plugin to your router instance so your router
dispatches actions on transition start, error, success and cancel (You can read more about router5 plugins here).
It also relay navigateTo
actions to the router.
If you are using immutable-js and redux-immutable simply use the reducer from 'redux-router5/immutable/reducer'
import { router5Middleware } from 'redux-router5';
import router5Reducer from 'redux-router5/immutable/reducer';
router5Middleware
redux middleware is optional.
The sole purpose of the redux middleware router5Middleware
is to translate actions to router instructions (navigate
, cancel
, canActivate
and canDeactivate
. You may not need it: for instance, if you use React and have your router instance in context, you can call router.navigate()
directly.
In that case, register reduxPlugin
with your router instance (when you use the middleware, that plugin is added for you).
import { reduxPlugin } from 'redux-router5';
// You need a router instance and a store instance
router.usePlugin(reduxPlugin(store.dispatch));
A simple reducer which is added by router5Middleware
. Note: use router
for your reducer key name, other names are not yet supported.
router5Reducer
will manage a piece of your state containing the following data attributes:
- route
- previousRoute
- transitionRoute (the current transitioning route)
- transitionError (the last error which occured)
route
and previousRoute
have a name
, params
and path
properties.
Available actions (you can use your router instance directly instead)
- navigateTo(routeName, routeParams = {}, routeOptions = {})
- cancelTransition()
- clearErrors()
- canActivate(routeName, true | false)
- canDeactivate(routeName, true | false)
import { actions } from 'redux-router5';
In order to use routeNodeSelector efficiently, you need react-redux >= 4.4.0 to be able to perform per component instance memoization.
routeNodeSelector
is a selector designed to be used on a route node and works with connect
higher-order component from react-redux
.
If your routes are nested, you'll have a few route nodes in your application. On each route change, only one route node needs to be re-rendered.
That route node is the highest common node between your previous route and your current route. routeNodeSelector
will only trigger a re-render
when it needs to.
Then it is just a matter of returning the right component depending on the current route. Your virtual tree will react to route changes, all of that by simply leveraging the power of connect and reselect!
router5.helpers provides a set of functions to help making those decisions (useful if you have nested routes).
import { connect } from 'react-redux';
import { routeNodeSelector } from 'redux-router5';
import { Home, About, Contact, Admin, NotFound } from './components';
import { startsWithSegment } from 'router5.helpers';
function Root({ route }) {
const { params, name } = route;
const testRoute = startsWithSegment(name);
if (testRoute('home')) {
return <Home params={ params } />;
} else if (testRoute('about')) {
return <About params={ params } />;
} else if (testRoute('contact')) {
return <Contact params={ params } />;
} else if (testRoute('admin')) {
return <Admin params={ params } />;
} else {
return <NotFound />;
}
}
export default connect(state => routeNodeSelector(''))(Root);
When using routeNodeSelector
with other connect properties:
export default connect(state => {
const selector = routeNodeSelector('');
return (state) => ({
a: state.a,
b: state.b,
...selector(state)
})
)(Root);