Convert Figma logo to code with AI

acdlite logoredux-router

Redux bindings for React Router – keep your router state inside your Redux store

2,303
216
2,303
31

Top Related Projects

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

A Redux binding for React Router v4

🎖 seamless redux-first routing -- just dispatch actions

🔥 A highly scalable, offline-first foundation with the best developer experience and a focus on performance and best practices.

Get started with React, Redux, and React-Router.

A starter boilerplate for a universal webapp using express, react, redux, webpack, and react-transform

Quick Overview

Redux Router is a routing library for React applications that integrates with Redux. It synchronizes your router state with your Redux store, allowing you to control your application's routing through Redux actions and access route information from your components.

Pros

  • Seamless integration with Redux, maintaining a single source of truth for application state
  • Allows for time-travel debugging of routing changes
  • Provides a declarative API for defining routes
  • Supports nested routes and route parameters

Cons

  • No longer actively maintained (last commit was in 2016)
  • May not be compatible with the latest versions of React and Redux
  • Limited documentation and community support due to its inactive status
  • More complex setup compared to other routing solutions like React Router

Code Examples

  1. Configuring routes:
import { Route } from 'redux-router';

<Route path="/" component={App}>
  <Route path="about" component={About} />
  <Route path="users/:userId" component={User} />
</Route>
  1. Dispatching a navigation action:
import { pushState } from 'redux-router';

dispatch(pushState(null, '/users/123'));
  1. Accessing route params in a component:
import { connect } from 'react-redux';

function User({ params }) {
  return <div>User ID: {params.userId}</div>;
}

export default connect(state => ({
  params: state.router.params
}))(User);

Getting Started

  1. Install the package:
npm install redux-router
  1. Set up the reducer and middleware:
import { reduxReactRouter, routerStateReducer } from 'redux-router';
import { createStore, combineReducers, applyMiddleware } from 'redux';
import { createHistory } from 'history';

const reducer = combineReducers({
  router: routerStateReducer,
  // ... other reducers
});

const store = compose(
  applyMiddleware(/* ... your middlewares */),
  reduxReactRouter({ createHistory })
)(createStore)(reducer);
  1. Wrap your app with ReduxRouter:
import { ReduxRouter } from 'redux-router';

function App() {
  return (
    <Provider store={store}>
      <ReduxRouter>
        {/* Your routes */}
      </ReduxRouter>
    </Provider>
  );
}

Competitor Comparisons

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

Pros of react-router-redux

  • Officially supported by the React Router team
  • Seamless integration with React Router v4+
  • More active development and community support

Cons of react-router-redux

  • Slightly more complex setup compared to redux-router
  • Requires additional configuration for time travel debugging

Code Comparison

redux-router:

import { reduxReactRouter, routerStateReducer, ReduxRouter } from 'redux-router';

const store = compose(
  applyMiddleware(thunk),
  reduxReactRouter({ routes })
)(createStore)(reducer);

<ReduxRouter />

react-router-redux:

import { routerMiddleware, ConnectedRouter } from 'react-router-redux';

const middleware = routerMiddleware(history);
const store = createStore(rootReducer, applyMiddleware(middleware));

<ConnectedRouter history={history}>
  <App />
</ConnectedRouter>

Summary

react-router-redux offers better integration with modern React Router versions and has more active development. However, it may require a bit more setup and configuration. redux-router provides a simpler implementation but lacks some advanced features and ongoing support. The choice between the two depends on specific project requirements and the desired level of integration with React Router.

A Redux binding for React Router v4

Pros of connected-react-router

  • More actively maintained and updated
  • Better integration with React Router v4 and v5
  • Supports time travel debugging with Redux DevTools

Cons of connected-react-router

  • Slightly more complex setup process
  • May have a steeper learning curve for beginners
  • Requires additional dependencies (history package)

Code Comparison

redux-router:

import { reduxReactRouter, routerStateReducer, ReduxRouter } from 'redux-router';

const store = compose(
  applyMiddleware(thunk),
  reduxReactRouter({ routes })
)(createStore)(reducer);

connected-react-router:

import { connectRouter, routerMiddleware } from 'connected-react-router';

const store = createStore(
  connectRouter(history)(rootReducer),
  applyMiddleware(routerMiddleware(history))
);

Both libraries aim to integrate Redux with React Router, but connected-react-router offers more modern features and better compatibility with recent versions of React Router. It provides a more seamless integration between Redux and routing, allowing for easier management of route-related state in Redux. However, redux-router may be simpler to set up for basic use cases and might be preferred by developers who are already familiar with its API.

🎖 seamless redux-first routing -- just dispatch actions

Pros of redux-first-router

  • More lightweight and focused solely on routing
  • Simpler API with less boilerplate code
  • Better integration with Redux, treating routes as actions

Cons of redux-first-router

  • Less mature and potentially less stable than redux-router
  • Smaller community and ecosystem
  • May require more manual configuration for complex routing scenarios

Code Comparison

redux-first-router:

const routesMap = {
  HOME: '/',
  USER: '/user/:id',
  CATEGORY: '/category/:name'
}

const { reducer, middleware, enhancer } = connectRoutes(routesMap)

redux-router:

<ReduxRouter>
  <Route path="/" component={App}>
    <Route path="user/:id" component={User} />
    <Route path="category/:name" component={Category} />
  </Route>
</ReduxRouter>

redux-first-router takes a more declarative approach, defining routes as a simple object, while redux-router uses a component-based structure similar to React Router. redux-first-router's approach aligns more closely with Redux principles, treating routes as actions and integrating them directly into the Redux store. This can lead to cleaner code and easier management of routing state. However, redux-router's approach may be more familiar to developers accustomed to traditional React routing patterns.

🔥 A highly scalable, offline-first foundation with the best developer experience and a focus on performance and best practices.

Pros of react-boilerplate

  • More comprehensive solution, providing a full project structure and tooling setup
  • Includes features like code splitting, offline-first functionality, and i18n support
  • Active community and regular updates

Cons of react-boilerplate

  • Steeper learning curve due to its complexity and opinionated structure
  • May include unnecessary features for smaller projects
  • Requires more setup and configuration

Code Comparison

redux-router:

import { reduxReactRouter, routerStateReducer, ReduxRouter } from 'redux-router';

const store = compose(
  applyMiddleware(thunk),
  reduxReactRouter({ routes })
)(createStore)(reducer);

react-boilerplate:

import { ConnectedRouter } from 'connected-react-router/immutable';
import history from 'utils/history';

<Provider store={store}>
  <ConnectedRouter history={history}>
    <App />
  </ConnectedRouter>
</Provider>

redux-router focuses specifically on integrating Redux with React Router, while react-boilerplate provides a more comprehensive solution for building React applications. react-boilerplate includes routing functionality through react-router and connected-react-router, but also offers additional features and a complete project structure. The code comparison shows the different approaches to setting up routing in each project.

Get started with React, Redux, and React-Router.

Pros of react-redux-starter-kit

  • Comprehensive boilerplate with a full-featured development environment
  • Includes testing setup with Karma, Mocha, and Chai
  • Provides a scalable project structure for larger applications

Cons of react-redux-starter-kit

  • More complex and opinionated setup, which may be overwhelming for beginners
  • Potentially includes unnecessary dependencies for simpler projects
  • Requires more setup and configuration compared to redux-router

Code Comparison

redux-router focuses on routing integration with Redux:

import { reduxReactRouter, routerStateReducer, ReduxRouter } from 'redux-router';

const store = compose(
  applyMiddleware(thunk),
  reduxReactRouter({ routes })
)(createStore)(reducer);

react-redux-starter-kit provides a more comprehensive setup:

import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';

const store = createStore(
  rootReducer,
  initialState,
  compose(applyMiddleware(thunk), DevTools.instrument())
);

The react-redux-starter-kit offers a more complete development environment, while redux-router focuses specifically on integrating routing with Redux. Choose based on your project's complexity and requirements.

A starter boilerplate for a universal webapp using express, react, redux, webpack, and react-transform

Pros of react-redux-universal-hot-example

  • Comprehensive example with universal (isomorphic) rendering
  • Includes hot reloading for faster development
  • Demonstrates integration of multiple technologies (React, Redux, Express, Webpack)

Cons of react-redux-universal-hot-example

  • More complex setup due to its comprehensive nature
  • May include unnecessary features for simpler projects
  • Potentially steeper learning curve for beginners

Code Comparison

redux-router:

import { reduxReactRouter, routerStateReducer, ReduxRouter } from 'redux-router';

const store = compose(
  applyMiddleware(thunk),
  reduxReactRouter({ routes })
)(createStore)(reducer);

react-redux-universal-hot-example:

import { createStore, applyMiddleware, compose } from 'redux';
import createMiddleware from './middleware/clientMiddleware';
import { routerMiddleware } from 'react-router-redux';

const middleware = [createMiddleware(client), routerMiddleware(history)];
const store = createStore(reducer, initialState, compose(
  applyMiddleware(...middleware),
  window.devToolsExtension ? window.devToolsExtension() : f => f
));

The react-redux-universal-hot-example provides a more comprehensive setup with additional middleware and development tools integration, while redux-router focuses specifically on routing integration with Redux.

Convert Figma logo designs to code with AI

Visual Copilot

Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.

Try Visual Copilot

README

redux-router

build status npm version redux-router on discord

This project is experimental.

For bindings for React Router 1.x see here

In most cases, you don’t need any library to bridge Redux and React Router. Just use React Router directly.

Please check out the differences between react-router-redux and redux-router before using this library

Redux bindings for React Router.

  • Keep your router state inside your Redux Store.
  • Interact with the Router with the same API you use to interact with the rest of your app state.
  • Completely interoperable with existing React Router API. <Link />, router.transitionTo(), etc. still work.
  • Serialize and deserialize router state.
  • Works with time travel feature of Redux Devtools!
# installs version 2.x.x
npm install --save redux-router

Install the version 1.0.0 via the previous tag

npm install --save redux-router@previous

Why

React Router is a fantastic routing library, but one downside is that it abstracts away a very crucial piece of application state — the current route! This abstraction is super useful for route matching and rendering, but the API for interacting with the router to 1) trigger transitions and 2) react to state changes within the component lifecycle leaves something to be desired.

It turns out we already solved these problems with Flux (and Redux): We use action creators to trigger state changes, and we use higher-order components to subscribe to state changes.

This library allows you to keep your router state inside your Redux store. So getting the current pathname, query, and params is as easy as selecting any other part of your application state.

Example

import React from 'react';
import { combineReducers, applyMiddleware, compose, createStore } from 'redux';
import { reduxReactRouter, routerStateReducer, ReduxRouter } from 'redux-router';
import { createHistory } from 'history';
import { Route } from 'react-router';

// Configure routes like normal
const routes = (
  <Route path="/" component={App}>
    <Route path="parent" component={Parent}>
      <Route path="child" component={Child} />
      <Route path="child/:id" component={Child} />
    </Route>
  </Route>
);

// Configure reducer to store state at state.router
// You can store it elsewhere by specifying a custom `routerStateSelector`
// in the store enhancer below
const reducer = combineReducers({
  router: routerStateReducer,
  //app: rootReducer, //you can combine all your other reducers under a single namespace like so
});

// Compose reduxReactRouter with other store enhancers
const store = compose(
  applyMiddleware(m1, m2, m3),
  reduxReactRouter({
    routes,
    createHistory
  }),
  devTools()
)(createStore)(reducer);


// Elsewhere, in a component module...
import { connect } from 'react-redux';
import { push } from 'redux-router';

connect(
  // Use a selector to subscribe to state
  state => ({ q: state.router.location.query.q }),

  // Use an action creator for navigation
  { push }
)(SearchBox);

You will find a server-rendering example in the repo´s example directory.

Works with Redux Devtools (and other external state changes)

redux-router will notice if the router state in your Redux store changes from an external source other than the router itself — e.g. the Redux Devtools — and trigger a transition accordingly!

Differences with react-router-redux

react-router-redux

react-router-redux (formerly redux-simple-router) takes a different approach to integrating routing with redux. react-router-redux lets React Router do all the heavy lifting and syncs the url data to a history location object in the store. This means that users can use React Router's APIs directly and benefit from the wide array of documentation and examples there.

The README for react-router-redux has a useful picture included here:

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

This approach, while simple to use, comes with a few caveats:

  1. The history location object does not include React Router params and they must be either passed down from a React Router component or recomputed.
  2. It is discouraged (and dangerous) to connect the store data to a component because the store data potentially updates after the React Router properties have changed, therefore there can be race conditions where the location store data differs from the location object passed down via React Router to components.

react-router-redux encourages users to use props directly from React Router in the components (they are passed down to any rendered route components). This means that if you want to access the location data far down the component tree, you may need to pass it down or use React's context feature.

redux-router

This project, on the other hand takes the approach of storing the entire React Router data inside the redux store. This has the main benefit that it becomes impossible for the properties passed down by redux-router to the components in the Route to differ from the data included in the store. Therefore feel free to connect the router data to any component you wish. You can also access the route params from the store directly. redux-router also provides an API for hot swapping the routes from the Router (something React Router does not currently provide).

The picture of redux-router would look more like this:

redux (store.router)  ↔  redux-router  ↔  react-router (via RouterContext)

This approach, also has its set of limitations:

  1. The router data is not all serializable (because Components and functions are not direclty serializable) and therefore this can cause issues with some devTools extensions and libraries that help in saving the store to the browser session. This can be mitigated if the libraries offer ways to ignore seriliazing parts of the store but is not always possible.
  2. redux-router takes advantage of the RouterContext to still use much of React Router's internal logic. However, redux-router must still implement many things that React Router already does on its own and can cause delays in upgrade paths.
  3. redux-router must provide a slightly different top level API (due to 2) even if the Route logic/matching is identical

Ultimately, your choice in the library is up to you and your project's needs. react-router-redux will continue to have a larger support in the community due to its inclusion into the reactjs github organization and visibility. react-router-redux is the recommended approach for react-router and redux integration. However, you may find that redux-router aligns better with your project's needs. redux-router will continue to be mantained as long as demand exists.

API

reduxReactRouter({ routes, createHistory, routerStateSelector })

A Redux store enhancer that adds router state to the store.

routerStateReducer(state, action)

A reducer that keeps track of Router state.

<ReduxRouter>

A component that renders a React Router app using router state from a Redux store.

push(location)

An action creator for history.push(). mjackson/history/docs/GettingStarted.md#navigation

Basic example (let say we are at http://example.com/orders/new):

dispatch(push('/orders/' + order.id));

Provided that order.id is set and equals 123 it will change browser address bar to http://example.com/orders/123 and appends this URL to the browser history (without reloading the page).

A bit more advanced example:

dispatch(push({
  pathname: '/orders',
  query: { filter: 'shipping' }
}));

This will change the browser address bar to http://example.com/orders?filter=shipping.

NOTE: clicking back button will change address bar back to http://example.com/orders/new but will not change page content

replace(location)

An action creator for history.replace(). mjackson/history/docs/GettingStarted.md#navigation

Works similar to the push except that it doesn't create new browser history entry.

NOTE: Referring to the push example: clicking back button will change address bar back to the URL before http://example.com/orders/new and will change page content.

go(n) goBack() goForward()

// Go back to the previous entry in browser history.
// These lines are synonymous.
history.go(-1);
history.goBack();

// Go forward to the next entry in browser history.
// These lines are synonymous.
history.go(1);
history.goForward();

Handling authentication via a higher order component

@joshgeller threw together a good example on how to handle user authentication via a higher order component. Check out joshgeller/react-redux-jwt-auth-example

Bonus: Reacting to state changes with redux-rx

This library pairs well with redux-rx to trigger route transitions in response to state changes. Here's a simple example of redirecting to a new page after a successful login:

const LoginPage = createConnector(props$, state$, dispatch$, () => {
  const actionCreators$ = bindActionCreators(actionCreators, dispatch$);
  const push$ = actionCreators$.map(ac => ac.push);

  // Detect logins
  const didLogin$ = state$
    .distinctUntilChanged(state => state.loggedIn)
    .filter(state => state.loggedIn);

  // Redirect on login!
  const redirect$ = didLogin$
    .withLatestFrom(
      push$,
      // Use query parameter as redirect path
      (state, push) => () => push(state.router.query.redirect || '/')
    )
    .do(go => go());

  return combineLatest(
    props$, actionCreators$, redirect$,
    (props, actionCreators) => ({
      ...props,
      ...actionCreators
    });
});

A more complete example is forthcoming.

NPM DownloadsLast 30 Days