redux-first-history vs connected-react-router vs react-router vs react-router-redux
React 路由管理库
redux-first-historyconnected-react-routerreact-routerreact-router-redux类似的npm包:

React 路由管理库

这些库用于在 React 应用程序中实现路由管理,帮助开发者在单页应用(SPA)中处理页面导航和状态管理。它们提供了不同的功能和设计理念,以满足不同的开发需求和场景。

npm下载趋势

3 年

GitHub Stars 排名

统计详情

npm包名称
下载量
Stars
大小
Issues
发布时间
License
redux-first-history90,52145399.3 kB152 年前MIT
connected-react-router04,700444 kB176-MIT
react-router056,2524.14 MB1561 个月前MIT
react-router-redux07,772-19 年前MIT

功能对比: redux-first-history vs connected-react-router vs react-router vs react-router-redux

路由同步

  • redux-first-history:

    redux-first-history 通过将历史记录与 Redux 结合,允许开发者在 Redux 中管理历史记录。它提供了对历史记录的完全控制,适合需要自定义历史管理的应用。

  • connected-react-router:

    connected-react-router 提供了与 Redux 的深度集成,确保路由状态与 Redux 状态同步。每当路由变化时,它会自动更新 Redux store,允许开发者在应用中轻松访问和管理路由状态。

  • react-router:

    react-router 本身不与 Redux 直接集成,但可以通过 props 传递路由信息。它允许开发者在组件中使用路由信息,但需要手动管理状态。

  • react-router-redux:

    react-router-redux 允许将 React Router 的路由状态与 Redux store 结合使用,提供了一种简单的方式来跟踪路由变化。它不提供深度集成,但可以方便地在 Redux 中访问路由信息。

学习曲线

  • redux-first-history:

    redux-first-history 的学习曲线可能较陡,因为它涉及到对历史记录的管理和 Redux 的结合。开发者需要理解如何在 Redux 中处理历史记录。

  • connected-react-router:

    connected-react-router 的学习曲线相对较陡,因为它需要理解 Redux 的工作原理以及如何将其与路由结合使用。对于初学者来说,可能需要一些时间来掌握。

  • react-router:

    react-router 的学习曲线较平缓,文档丰富,易于上手。开发者可以快速理解路由的基本概念和用法,适合初学者。

  • react-router-redux:

    react-router-redux 的学习曲线介于 connected-react-router 和 react-router 之间。虽然它提供了路由与 Redux 的结合,但需要对 Redux 有一定的了解。

灵活性

  • redux-first-history:

    redux-first-history 提供了高度的灵活性,允许开发者自定义历史记录管理,适合需要特殊历史处理的应用。

  • connected-react-router:

    connected-react-router 提供了灵活的路由管理能力,能够与 Redux 的状态管理无缝集成,适合需要复杂路由逻辑的应用。

  • react-router:

    react-router 是一个灵活的路由库,允许开发者根据需要自由配置路由,支持嵌套路由和动态路由,非常适合各种规模的应用。

  • react-router-redux:

    react-router-redux 提供了一种灵活的方式来将路由状态与 Redux 结合,但不提供直接的路由管理功能,适合需要在 Redux 中跟踪路由的应用。

维护性

  • redux-first-history:

    redux-first-history 的维护性较高,因为它允许开发者完全控制历史记录的管理,适合需要长期维护的复杂应用。

  • connected-react-router:

    connected-react-router 的维护性较高,因为它遵循 Redux 的设计理念,提供清晰的状态管理和路由同步机制,适合大型应用的长期维护。

  • react-router:

    react-router 的维护性良好,文档齐全,社区活跃,开发者可以轻松找到解决方案和支持。

  • react-router-redux:

    react-router-redux 的维护性依赖于 Redux 的状态管理,虽然提供了一定的路由跟踪能力,但可能需要额外的工作来保持路由与状态的一致性。

性能

  • redux-first-history:

    redux-first-history 的性能表现良好,能够高效管理历史记录,适合需要复杂历史管理的应用。

  • connected-react-router:

    connected-react-router 的性能表现良好,能够高效地同步路由状态与 Redux store,适合需要频繁路由变化的应用。

  • react-router:

    react-router 的性能表现优异,能够快速处理路由变化,适合大多数应用场景。

  • react-router-redux:

    react-router-redux 的性能表现依赖于 Redux 的性能,能够有效地跟踪路由变化,但可能在复杂应用中引入额外的性能开销。

如何选择: redux-first-history vs connected-react-router vs react-router vs react-router-redux

  • redux-first-history:

    选择 redux-first-history 如果你希望在 Redux 中管理历史记录并实现路由功能,同时保持对历史记录的完全控制。它适合需要自定义历史管理的应用,提供了更灵活的历史记录处理方式。

  • connected-react-router:

    选择 connected-react-router 如果你需要将 React Router 与 Redux 状态管理结合使用,以便在 Redux 中同步路由状态。这对于需要在应用程序中保持路由状态一致性的复杂应用非常有用。

  • react-router:

    选择 react-router 如果你只需要一个简单且强大的路由解决方案。它是最常用的 React 路由库,适合大多数应用场景,提供灵活的路由配置和嵌套路由功能。

  • react-router-redux:

    选择 react-router-redux 如果你想将 React Router 的路由状态与 Redux 状态管理结合使用,但不需要 connected-react-router 提供的深度集成。它允许你在 Redux 中跟踪路由变化,但不提供路由的直接连接。

redux-first-history的README

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.