Skip to content

lagunovsky/redux-react-router

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

78 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

License TypeScript Tests workflow

Redux React Router

A Redux binding for React Router

Main features

  • 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's connect.
  • Supports time traveling in Redux DevTools.
  • TypeScript

Installation

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

Usage

Examples

Note: the history object provided to reducer, middleware, and component must be the same history object.

@reduxjs/toolkit

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>
  )
}

redux.createStore and custom selector

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>
  )
}

API

Types

type ReduxRouterProps = {
  history: History
  basename?: string
  children?: React.ReactNode
  routerSelector?: ReduxRouterSelector 
}
type ReduxRouterState = {
  location: history.Location
  action: history.Action
}

Constants

const ROUTER_REDUCER_MAP_KEY = 'router'
const ROUTER_CALL_HISTORY_METHOD = '@@router/CALL_HISTORY_METHOD'
const ROUTER_ON_LOCATION_CHANGED = '@@router/ON_LOCATION_CHANGED'

createRouterMiddleware(history: History) => Middleware

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.

createRouterReducerMapObject(history: History) => {router: Reducer<ReduxRouterState>}

Creates a reducer map object that stores location updates from history.

createRouterReducer(history: History) => Reducer<ReduxRouterState>

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.

reduxRouterSelector(state: State): ReduxRouterState

Selector that returns location updates from history.

Action creators

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

Migrate from Connected React Router

- 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'

About

A Redux binding for React Router v6

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors 4

  •  
  •  
  •  
  •