Redux First History - Make Redux 100% SINGLE-AND-ONLY source of truth again!
Use whatever you like. History will just work as it should.
//react-router v4
this.context.router.history.location === state.router.location
this.context.route.location === state.router.location
this.props.history.location === state.router.location
this.props.location === state.router.location
withRouter.props.location === state.router.location
//@reach/router
this.props.location === state.router.location
You can mix redux, redux-saga, react-router & @reach/router
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 issue!
Demo : https://wy5qw1125l.codesandbox.io/ src: https://codesandbox.io/s/wy5qw1125l
Using npm:
$ npm install --save redux-first-history
Or yarn:
$ yarn add redux-first-history
store.js
import { createStore, combineReducers, applyMiddleware } from "redux";
import { composeWithDevTools } from "redux-devtools-extension";
import { createReduxHistoryContext, reachify } from "redux-first-history";
import createHistory from "history/createBrowserHistory";
const { createReduxHistory, routerMiddleware, routerReducer} = createReduxHistoryContext({
history: createHistory(),
//others 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);
app.js
import React, { Component } from "react";
import { Provider, connect } from "react-redux";
import { push } from "redux-first-history";
import { Router } from "react-router-dom";
import { store, history } from "./store";
const App=() => (
<Provider store={store}>
<Router history={history}>
//.....
</Router>
</Provider>
);
export default App;
- just simple Router with no more ConnectedRouter!
- use
push
action creator fromredux-first-history
if you need to dispatch location fromsaga
or connected components. - Probably, you already did it with
react-router-redux
orconnected-react-router
(in this case you have only to replace the import!)
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 store, others that take location from router context, and others from withRouter HOC. If you are on a big crazy already-messed-up project, you could see also connect(withRouter) or withRouter(connect) ....
This sometimes could generate sync issue, due to the fact that many components are updated at different time. 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 !
- 100% one source of truth (store)
- No syncronization depending on rendering lifecicle (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-router v4
- support @reach/router 1.2.1
- support mix react-router & @reach-router in the same app.
- fast migration from existing project, with same
LOCATION_CHANGE
and push actions (taken from RRR) - handle Redux Travelling from devTools (that's a non sense in production, but at the end of the day this decision it's up to you ...)
- custom opions and blazing fast (ok, every lib should have these features, that's true :D)
export const createReduxHistoryContext = ({
history, routerReducerKey = 'router', oldLocationChangePayload = false, reduxTravelling = false,
})
key | optional | description |
---|---|---|
history | no | The history/createBrowserHistory object - currently tested only with version 4.7.2 |
routerReducerKey | yes | if you don't like router name for reducer. |
oldLocationChangePayload | yes | if you use the oldLOCATION_CHANGE payload{ ...location } instead of the new{ location} , setting this flag will make you able to not change your app at all! |
reduxTravelling | yes | if you want to play with redux-dev-tools :D. |
Let me know what do you think!
Enjoy it? Star this project! :D
- test, test and test!
- code clean and split
- a better way to compile with rollup
- warning for uncorrect usage
- typescript support
- try to use it with non-react-like other framework!
- redux-first-app POC
any idea? let me know and contribute!
- redux-first-routing
- react-router-redux
- connected-react-router
See Contributors.