Achieve client-side routing the Redux way:
- Read location data from the store.
- Update the location by dispatching navigation actions.
- Let middleware handle the side-effect of history navigation.
Learn more: An Introduction to the Redux-First Routing Model
This library wraps history
and provides a minimal, framework-agnostic base for accomplishing Redux-first routing. It does not provide the actual router component.
Instead, you can pair it with a compatible router to create a complete routing solution. If you're coming from React Router, you might compare this package to react-router-redux
.
Install the library from npm:
npm install --save redux-first-routing
Or, use the following script tag to access the latest UMD build on window.ReduxFirstRouting
:
<script src="https://unpkg.com/redux-first-routing/dist/redux-first-routing.min.js"></script>
import { combineReducers, applyMiddleware, createStore } from 'redux'
import { createBrowserHistory, routerReducer, routerMiddleware, startListener, push } from 'redux-first-routing'
import { otherReducers } from './reducers'
// Create the history object
const history = createBrowserHistory()
// Add the reducer, which adds location state to the store
const rootReducer = combineReducers({
...otherReducers,
router: routerReducer // Convention is to use the "router" property
})
// Build the middleware, which intercepts navigation actions and calls the corresponding history method
const middleware = routerMiddleware(history)
// Create the store
const store = createStore(rootReducer, {}, applyMiddleware(middleware))
// Start the history listener, which automatically dispatches actions to keep the store in sync with the history
startListener(history, store)
// Now you can read the location from the store!
let currentLocation = store.getState().router.pathname
// You can also subscribe to changes in the location!
let unsubscribe = store.subscribe(() => {
let previousLocation = currentLocation
currentLocation = store.getState().router.pathname
if (previousLocation !== currentLocation) {
console.log(`Location changed from ${previousLocation} to ${currentLocation}`)
// Render your application reactively here (optionally using a compatible router)
}
})
// And you can dispatch navigation actions from anywhere!
store.dispatch(push('/about'))
There are dozens of ways to design the state shape of the location data, and this project must by nature choose an opinionated design:
// URL: www.example.com/nested/path?with=query#and-hash
{
router: {
pathname: '/nested/path/',
search: '?with=query',
queries: {
with: 'query'
},
hash: '#and-hash'
},
... // other redux state
}
The query-string
package is used internally to parse the search
string into the queries
object.
For a router to work seamlessly with redux-first-routing
, it must not be tightly coupled to the browser history/navigation code. The following libraries meet that criteria by providing router components that focus solely on the routing and/or rendering part:
- Universal Router (framework-agnostic)
- Redux JSON Router (React)
For examples of usage, see Recipies. To add to this list, feel free to send a pull request.
- API
- Recipies
The concept of "Redux-first routing" isn't particularly new — everything in this library has existed in one form or another across various other Redux routing libraries and packages. You may find a long list of similar projects (some of which may be classified as Redux-first routing libraries) here:
Notable influences:
Contributions are welcome and are greatly appreciated! 🎉
Feel free to file an issue, start a discussion, or send a pull request.
MIT