A Redux binding for React Router
- Synchronize router state with redux store through uni-directional flow (i.e. history -> store -> router -> components).
- Supports React Router v6 and History v5
- Supports functional component hot reloading while preserving state.
- Dispatching of history methods (
push
,replace
,go
,back
,forward
) works for both redux-thunk and redux-saga. - Nested children can access routing state such as the current location directly with
react-redux
'sconnect
. - Supports time traveling in Redux DevTools.
- TypeScript
Redux React Router requires React 16.8, React Redux 6.0, React Router 6.0 or later.
npm install --save @lagunovsky/redux-react-router
yarn add @lagunovsky/redux-react-router
Note: the history
object provided to reducer, middleware, and component must be the same history
object.
import { createRouterMiddleware, createRouterReducerMapObject, push, ReduxRouter } from '@lagunovsky/redux-react-router'
import { configureStore } from '@reduxjs/toolkit'
import { createBrowserHistory } from 'history'
import React from 'react'
import { createRoot } from 'react-dom/client'
import { Provider, useDispatch } from 'react-redux'
import { Route, Routes } from 'react-router'
import { NavLink } from 'react-router-dom'
const history = createBrowserHistory()
const routerMiddleware = createRouterMiddleware(history)
const store = configureStore({
reducer: createRouterReducerMapObject(history),
middleware: (getDefaultMiddleware) => getDefaultMiddleware().prepend(routerMiddleware),
})
function Content() {
const dispatch = useDispatch()
const onClickHandler = () => {
const action = push(Date.now().toString())
dispatch(action)
}
return (
<>
<NavLink to={'/'} children={'Home'}/>
<button type={'button'} onClick={onClickHandler} children={'Rand'}/>
</>
)
}
function App() {
return (
<Provider store={store}>
<ReduxRouter history={history}>
<Routes>
<Route path={'*'} element={<Content/>}/>
</Routes>
</ReduxRouter>
</Provider>
)
}
import { createRouterMiddleware, createRouterReducer, push, ReduxRouter, ReduxRouterSelector } from '@lagunovsky/redux-react-router'
import { createBrowserHistory } from 'history'
import React from 'react'
import { createRoot } from 'react-dom/client'
import { Provider, useDispatch } from 'react-redux'
import { Route, Routes } from 'react-router'
import { NavLink } from 'react-router-dom'
import { applyMiddleware, combineReducers, compose, createStore } from 'redux'
const history = createBrowserHistory()
const routerMiddleware = createRouterMiddleware(history)
const rootReducer = combineReducers({ navigator: createRouterReducer(history) })
const store = createStore(rootReducer, compose(applyMiddleware(routerMiddleware)))
type State = ReturnType<typeof store.getState>
const routerSelector: ReduxRouterSelector<State> = (state) => state.navigator
function App() {
return (
<Provider store={store}>
<ReduxRouter history={history} routerSelector={routerSelector}>
<Routes>
<Route path={'*'} element={<div/>}/>
</Routes>
</ReduxRouter>
</Provider>
)
}
type ReduxRouterProps = {
history: History
basename?: string
children?: React.ReactNode
routerSelector?: ReduxRouterSelector
}
type ReduxRouterState = {
location: history.Location
action: history.Action
}
const ROUTER_REDUCER_MAP_KEY = 'router'
const ROUTER_CALL_HISTORY_METHOD = '@@router/CALL_HISTORY_METHOD'
const ROUTER_ON_LOCATION_CHANGED = '@@router/ON_LOCATION_CHANGED'
A middleware you can apply to your Redux store to capture dispatched actions created by the action creators. It will redirect those actions to the provided history instance.
Creates a reducer map object that stores location updates from history.
Creates a reducer function that stores location updates from history.
Note: If you create a reducer with a key other than ROUTER_REDUCER_MAP_KEY
,
you must add a selector (state: State) => ReduxRouterState
to your <ReduxRouter/>
as routerSelector
prop.
Selector that returns location updates from history.
Same as history methods
push
replace
go
back
forward
By default, history methods calls in middleware are wrapped in a queueMicrotask
. If you want to avoid it, please use the following methods:
pushStraight
replaceStraight
goStraight
backStraight
forwardStraight
- import { connectRouter } from 'connected-react-router'
+ import { createRouterReducer } from '@lagunovsky/redux-react-router'
- export const routerReducer = connectRouter(history)
+ export const routerReducer = createRouterReducer(history)
- import { routerMiddleware } from 'connected-react-router'
+ import { createRouterMiddleware } from '@lagunovsky/redux-react-router'
- export const routerMiddleware = routerMiddleware(history)
+ export const routerMiddleware = createRouterMiddleware(history)
- import { ConnectedRouter } from 'connected-react-router'
+ import { ReduxRouter } from '@lagunovsky/redux-react-router'
- <ConnectedRouter history={history} />
+ <ReduxRouter history={history} />
- import { RouterState } from 'connected-react-router'
+ import { ReduxRouterState } from '@lagunovsky/redux-react-router'