redux-first-history vs connected-react-router vs react-router vs react-router-redux vs redux-logger
React Router and Redux Integration
redux-first-historyconnected-react-routerreact-routerreact-router-reduxredux-loggerSimilar Packages:

React Router and Redux Integration

React Router and Redux Integration libraries help manage navigation and state in React applications. They provide tools to synchronize the browser's history with the application's state, allowing for seamless navigation and state management. These libraries are essential for building complex applications where routing and state need to work together efficiently. They enable features like navigating based on state changes, persisting state across routes, and handling asynchronous actions related to routing. By integrating routing with Redux, developers can create more predictable and maintainable applications, making it easier to manage both UI and data state in a cohesive manner.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
redux-first-history91,35345399.3 kB152 years agoMIT
connected-react-router04,699444 kB176-MIT
react-router056,2854.18 MB15312 days agoMIT
react-router-redux07,768-19 years agoMIT
redux-logger05,734-589 years agoMIT

Feature Comparison: redux-first-history vs connected-react-router vs react-router vs react-router-redux vs redux-logger

Routing Integration with Redux

  • redux-first-history:

    redux-first-history provides a Redux-first approach to managing history and navigation. It allows you to integrate history management directly into your Redux store, giving you more control over navigation actions and state without tightly coupling it with React Router.

  • connected-react-router:

    connected-react-router integrates React Router with Redux by synchronizing the router state with the Redux store. It allows you to dispatch navigation actions (e.g., push, replace) as Redux actions, making routing state predictable and easier to manage.

  • react-router:

    react-router is a standalone routing library that does not require Redux. It provides a flexible API for managing routes, nested routes, and dynamic routing. It is highly customizable and can be used in any React application without state management dependencies.

  • react-router-redux:

    react-router-redux was designed to synchronize the router state with Redux. However, it is no longer actively maintained, and its approach has been largely replaced by connected-react-router, which offers a more robust integration between React Router and Redux.

Ease of Use

  • redux-first-history:

    redux-first-history offers a straightforward API for managing history and navigation within a Redux context. It is easy to integrate and use, especially for developers familiar with Redux patterns.

  • connected-react-router:

    connected-react-router is easy to use once integrated with Redux. It requires setting up a Redux store and middleware, but it provides a clear API for dispatching navigation actions and accessing router state from the store.

  • react-router:

    react-router is known for its intuitive API and ease of use. It provides clear documentation and examples, making it easy for developers to implement routing in their applications without a steep learning curve.

  • react-router-redux:

    react-router-redux was relatively easy to use for synchronizing router state with Redux, but its lack of maintenance and updates makes it less reliable for new projects. Developers are encouraged to use connected-react-router instead.

Code Example

  • redux-first-history:

    Example of redux-first-history

    import { createStore } from 'redux';
    import { Provider } from 'react-redux';
    import { Router } from 'react-router';
    import { createBrowserHistory } from 'history';
    import { createReduxHistory } from 'redux-first-history';
    import rootReducer from './reducers';
    import routes from './routes';
    
    const history = createBrowserHistory();
    const reduxHistory = createReduxHistory(history);
    const store = createStore(rootReducer);
    
    const App = () => (
      <Provider store={store}>
        <Router history={reduxHistory}>{routes}</Router>
      </Provider>
    );
    
    export default App;
    
  • connected-react-router:

    Example of connected-react-router

    import { createStore, applyMiddleware } from 'redux';
    import { Provider } from 'react-redux';
    import { ConnectedRouter, routerMiddleware } from 'connected-react-router';
    import { createBrowserHistory } from 'history';
    import { Route } from 'react-router-dom';
    import rootReducer from './reducers';
    import App from './App';
    
    const history = createBrowserHistory();
    const store = createStore(rootReducer, applyMiddleware(routerMiddleware(history)));
    
    const Root = () => (
      <Provider store={store}>
        <ConnectedRouter history={history}>
          <Route path="/" component={App} />
        </ConnectedRouter>
      </Provider>
    );
    
    export default Root;
    
  • react-router:

    Example of react-router

    import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
    import Home from './Home';
    import About from './About';
    import NotFound from './NotFound';
    
    const App = () => (
      <Router>
        <Switch>
          <Route exact path="/" component={Home} />
          <Route path="/about" component={About} />
          <Route component={NotFound} />
        </Switch>
      </Router>
    );
    
    export default App;
    
  • react-router-redux:

    Example of react-router-redux

    import { createStore } from 'redux';
    import { Provider } from 'react-redux';
    import { Router } from 'react-router';
    import { syncHistoryWithStore } from 'react-router-redux';
    import createHistory from 'history/createBrowserHistory';
    import routes from './routes';
    import rootReducer from './reducers';
    
    const store = createStore(rootReducer);
    const history = createHistory();
    const syncedHistory = syncHistoryWithStore(history, store);
    
    const App = () => (
      <Provider store={store}>
        <Router history={syncedHistory}>{routes}</Router>
      </Provider>
    );
    
    export default App;
    

How to Choose: redux-first-history vs connected-react-router vs react-router vs react-router-redux vs redux-logger

  • redux-first-history:

    Choose redux-first-history if you want a Redux-centric approach to managing history and navigation. It provides a simple API for integrating history with Redux, allowing for more control over navigation actions and state management.

  • connected-react-router:

    Choose connected-react-router if you want to integrate React Router with Redux seamlessly. It keeps your router state in sync with your Redux store, allowing for more predictable navigation and easier testing.

  • react-router:

    Choose react-router if you need a powerful and flexible routing solution for your React applications. It provides a comprehensive set of features for managing routes, nested routing, and dynamic routing without any dependency on Redux.

  • react-router-redux:

    Choose react-router-redux if you are using an older version of React Router and Redux and need to synchronize the router state with the Redux store. Note that this package is no longer actively maintained, and you may want to consider alternatives like connected-react-router.

  • redux-logger:

    Choose redux-logger if you need a middleware for logging Redux actions and state changes. It is useful for debugging and monitoring the flow of actions in your application, but it does not directly relate to routing or navigation.

README for redux-first-history

redux-first-history


Redux First History - Make Redux 100% SINGLE-AND-ONLY source of truth!

Redux history binding for

Compatible with immer - redux-immer - redux-immutable.

:tada: A smaller, faster, optionated, issue-free alternative to connected-react-router

Table of Contents

Main Goal

While working with relatively large projects, it's quite common to use both redux and react-router.

So you may have components that take location from the redux store, others that take location from router context, and others from withRouter HOC.

This can generate sync issues, due to the fact that many components are updated at different times. In addition, React shallowCompare rendering optimization will not work as it should.

With redux-first-history, you can mix components that get history from wherever, they will always be tunneled to state.router.location !

Use whatever you like. History will just work as it should.

//react-router v5 - v6
useLocation() === state.router.location

//react-router v5
this.props.history.location === state.router.location
this.props.location === state.router.location
withRouter.props.location === state.router.location

//react-router v4
this.context.router.history.location === state.router.location
this.context.route.location === state.router.location

//@reach/router
this.props.location === state.router.location

//wouter - pathname
useLocation()[0] === state.router.location.pathname

Mix redux, redux-saga, react-router, @reach/router, wouter and react-location without any synchronization issue!
Why? Because there is no synchronization at all! There is only one history: reduxHistory!

  • One way data-flow
  • One unique source of truth
  • No more location issues!

Edit Redux-First Router Demo

Demo

Main Features

  • 100% one source of truth (store)
  • No synchronization depending on rendering lifecycle (ConnectedRouter)
  • No React dependency (we want history to be always in store!)
  • 100% one-way data flow (only dispatch actions!)
  • Improve React shallowCompare as there is only one "location"
  • Support react-location 3.x
  • Support react-router v4 / v5 / v6
  • Support @reach/router 1.x
  • Support wouter 2.x
  • Support mix react-router, @reach/router & wouter in the same app!
  • Fast migration from an existing project, with the same LOCATION_CHANGE and push actions (taken from RRR)
  • Handle Redux Travelling from dev tools (that's nonsense in production, but at the end of the day this decision it's up to you ...)

Installation

Using npm:

$ npm install --save redux-first-history

Or yarn:

$ yarn add redux-first-history

Usage

store.js

import { createStore, combineReducers, applyMiddleware } from "redux";
import { composeWithDevTools } from "redux-devtools-extension";
import { createReduxHistoryContext, reachify } from "redux-first-history";
import { createWouterHook } from "redux-first-history/wouter";
import { createBrowserHistory } from 'history';

const { createReduxHistory, routerMiddleware, routerReducer } = createReduxHistoryContext({ 
  history: createBrowserHistory(),
  //other options if needed 
});

export const store = createStore(
  combineReducers({
    router: routerReducer
    //... reducers //your reducers!
  }),
  composeWithDevTools(
    applyMiddleware(routerMiddleware)
  )
);

export const history = createReduxHistory(store);
//if you use @reach/router 
export const reachHistory = reachify(history);
//if you use wouter
export const wouterUseLocation = createWouterHook(history);

store.js (with @reduxjs/toolkit)

import { combineReducers } from "redux";
import { configureStore } from "@reduxjs/toolkit";
import { createReduxHistoryContext } from "redux-first-history";
import { createBrowserHistory } from "history";

const {
  createReduxHistory,
  routerMiddleware,
  routerReducer
} = createReduxHistoryContext({ history: createBrowserHistory() });

export const store = configureStore({
  reducer: combineReducers({
    router: routerReducer
  }),
  middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(routerMiddleware),
});

export const history = createReduxHistory(store);

app.js

import React, { Component } from "react";
import { Provider, connect } from "react-redux";
import { Router } from "react-router-dom";
import { store, history } from "./store";

const App = () => (
      <Provider store={store}>
        <Router history={history}>
        //.....
        </Router>
      </Provider>
    );

export default App;

app.js (react-router v6)

import React, { Component } from "react";
import { Provider } from "react-redux";
import { HistoryRouter as Router } from "redux-first-history/rr6";
import { store, history } from "./store";

const App = () => (
      <Provider store={store}>
        <Router history={history}>
        //.....
        </Router>
      </Provider>
    );

export default App;

saga.js (react-saga)

import { put } from "redux-saga/effects";
import { push } from "redux-first-history";

function* randomFunction() {
  //....
  yield put(push(YOUR_ROUTE_PATH));
  //....
}

slice.js (in a Thunk with @reduxjs/toolkit)

import { push } from "redux-first-history";

export const RandomThunk = (dispatch) => {
  //....
  dispatch(push(YOUR_ROUTE_PATH));
  //....
}
  • Just a simple Router with no more ConnectedRouter!
  • Probably, you already did connect the Redux store with react-router-redux or connected-react-router (in this case you have only to replace the import!)

Options

export const createReduxHistoryContext = ({
  history, 
  routerReducerKey = 'router', 
  reduxTravelling = false, 
  selectRouterState = null,
  savePreviousLocations = 0,
  batch = null,
  reachGlobalHistory = null
})
keyoptionaldescription
historynoThe createBrowserHistory object - v4.x/v5.x
routerReducerKeyyesif you don't like router name for reducer.
reduxTravellingyesif you want to play with redux-dev-tools :D.
selectRouterStateyescustom selector for router state. With redux-immutable selectRouterState = state => state.get("router")
savePreviousLocationsyesif > 0 add the key "previousLocation" to state.router, with the last N locations. [{location,action}, ...]
batchyesa batch function for batching states updates with history updates. Prevent top-down updates on react : usage import { unstable_batchedUpdates } from 'react-dom'; batch = unstable_batchedUpdates
reachGlobalHistoryyesglobalHistory object from @reach/router - support imperatively navigate of @reach/router - import { navigate } from '@reach/router' : usage import { globalHistory } from '@reach/router'; reachGlobalHistory = globalHistory
basenamenosupport basename (history v5 fix)

Advanced Config

  • Support "navigate" from @reach/router
import { createReduxHistoryContext, reachify } from "redux-first-history";
import { createBrowserHistory } from 'history';
import { globalHistory } from '@reach/router';

const { createReduxHistory, routerMiddleware, routerReducer } = createReduxHistoryContext({ 
  history: createBrowserHistory(),
  reachGlobalHistory: globalHistory,
  //other options if needed 
});
  • React batch updates: top-down batch updates for maximum performance. Fix also some updated edge cases.
import { createReduxHistoryContext, reachify } from "redux-first-history";
import { createBrowserHistory } from 'history';
import { unstable_batchedUpdates } from 'react-dom';

const { createReduxHistory, routerMiddleware, routerReducer } = createReduxHistoryContext({ 
  history: createBrowserHistory(),
  batch: unstable_batchedUpdates,
  //other options if needed 
});

Feedback

Let me know what do you think!
Enjoy it? Star this project! :D

Credits & Inspiration

  • redux-first-routing
  • react-router-redux
  • connected-react-router

Contributors

See Contributors.

License

MIT License.