redux-shunt
v0.0.5
Published
A redux store enhancer which diverts control of state from the store
Downloads
4
Maintainers
Readme
Redux Shunt
Redux shunt is a store enhancer which prevents the store's state from updating when an action is dispatched to it. Instead, both the action and the result of the reducers processing that action (what would have been the new state) are provided to a callback.
Additionally, redux shunt exposes an additional method on the store, setState
, which sets the store's entire state tree and notifies its listeners.
Installation:
npm install --save redux-shunt
Why Do I Need This?
Redux shunt breaks your redux store, so if you use it haphazardly you will most likely be disappointed!
The use-case for redux shunt is when you want to encapsulate an entire redux store, and use something else (like another redux store) as its source of truth.
Example usage
Let's say for example we have a redux app, SubApp
, which has a simple state tree:
interface SubAppState {
foo: boolean;
bar: boolean;
}
Say we have another redux app, MainApp
, in which we want to incorporate SubApp
:
interface MainAppState {
baz: boolean;
subAppState: SubAppState;
}
const mainAppInitialState = {
baz: true;
subAppState: {
foo: true,
bar: true
}
}
Additionally, we want baz of MainApp
's state to always be equal to bar from SubApp
and vice versa. Other than that, don't really want to worry about SubApp
's internals.
Assuming we have an action "SUB_APP_UPDATE"
which provides SubApp
's new state as the payload each time it should be updated, we can write our reducer for MainApp
like this:
const mainAppReducer = (state: MainAppState, action: AnyAction) => {
switch (action.type) {
case 'SUB_APP_UPDATE':
return {
baz: action.payload.bar,
subAppState: action.payload
};
case 'TOGGLE_BAZ':
return {
baz: !state.baz,
subAppState: { ...state.subAppState, bar: !state.baz }
};
default:
return state;
}
}
The problems here are:
- How do we dispatch an action to
MainApp
each timeSubApp
's state should update? - How do we ensure that
MainApp
takes full responsibility for the state ofSubApp
, and becomes its source of truth?
This is where redux shunt comes in!
We can shunt updates to SubApp
's state so that they are processed through MainApp
like so:
import { createStore } from 'redux';
import shunt from 'redux-shunt';
import { subAppReducer } from './subApp/internals/dontknow/dontcare';
// create normal store for the main app
const mainAppStore = createStore(mainAppReducer, mainAppInitialState);
// sub app's initial state comes from the main app's state tree
const subAppInitialState = mainAppStore.getState().subAppState;
// create a shunted store for the sub app, providing a callback to handle state updates
const subAppStore = shunt<SubAppState>(recipient)(createStore)(subAppReducer, subAppInitialState)
// refer to the real source of truth (MainApp) for the sub app's state
mainAppStore.subscribe(updateSubApp);
function recipient(action: AnyAction, state: SubAppState) {
// lift the new sub app state into the main app's state tree
mainAppStore.dispatch({
type: 'SUB_APP_UPDATE',
payload: state
});
}
function updateSubApp() {
const newSubAppState = mainAppStore.getState().subAppState;
// sub app's state is only really updated, and its listeners notified, when the state changes in MainApp
subAppStore.setState(newSubAppState);
}
That's it! SubApp
is now a component of MainApp
, and MainApp
's store is our single source of truth. Later you can follow the same pattern to use MainApp
as a component of an even larger app.
Contributions
Anyone is welcome to contribute to this package. My only "rule" is that your contribution must either pass the existing unit tests, or include additional unit tests to cover new functionality.
Here are some commands that may be helpful for development:
npm test
: Runs the unit testsnpm run build
: Builds the library
License
MIT