sub-redux v1.0.0-alpha.1
SubRedux
sub-redux is a library that allows you to dynamically create, use and destroy
multiple isolated redux 'sub-apps'.
Getting started
Install
$ npm install --save sub-reduxor
$ yarn add sub-reduxUsage Example
Include the SubRedux reducer and middleware in your main store:
import { applyMiddleware, combineReducers, createStore } from 'redux';
import {
middleware as subReduxMiddleware,
reducer as subReduxReducer,
} from 'sub-redux';
const rootReducer = combineReducers({
subRedux: subReduxReducer,
...otherStateSlices,
});
const store = createStore(rootReducer, applyMiddleware(subReduxMiddleware));Using a subStore
import createSagaMiddleware from 'redux-saga';
import { delay, put, takeEvery } from 'redux-saga/effects';
import { actions, getId, getSubStore } from 'sub-redux';
// Create it by dispatching an init action:
const instance = getId();
const sagaMiddleware = createSagaMiddleware();
store.dispatch(
actions.init({
instance,
initial: { count: 0 },
reducer: (state, action) => {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + 1 };
default:
return state;
}
},
middleware: [sagaMiddleware],
}),
);
const task = sagaMiddleware.run(function*() {
yield takeEvery('INCREMENT_LATER', function*() {
yield delay(3000);
yield put({ type: 'INCREMENT' });
});
});
const subStore = getSubStore(instance, store);
// Use it
subStore.getState();
subStore.dispatch({ type: 'INCREMENT_LATER' });
//The above is equivalent to:
store.getState().subRedux[instance].state;
store.dispatch({ type: `SUB_REDUX/${instance}/INCREMENT_LATER` });
// Destroy it once you're done
task.cancel();
store.dispatch(actions.destroy({ instance }));How it works
Dispatching the actions.init({ instance, initial, reducer, middleware })
action, two things happen:
- Your initial state and the reducer are stored in the main store's state as
subRedux[instance].stateandsubRedux[instance].reducer - The SubRedux middleware will take your provided middleware and build a chain out of it.
Once a subStore is initialised you can create a wrapper for it with
getSubStore(instance, store). Calling getState() on the subStore returns you
that state's store, and calling dispatch(action) on it will wrap that action
and dispatch it against the main store. Whenever a SUB_REDUX/${n}/${action} is
dispatched against the main store, subStore n will reduce and apply
middlewares accordingly.
When you're finished with your subStore, dispatching
actions.destroy({ instance }) against the main store will destroy it's state,
reducer and middleware.
React usage
Higher up in your component tree:
import { Provider } from 'react-redux';
<Provider store={yourMainStore}>
<YourApp />
</Provider>;Your component in which you wish to create a subStore:
function MyComponent() {
const sagaMiddleware = React.useMemo(
() => createSagaMiddleware(),
[], // Only ever create the one
);
const subStore = useSubRedux({
initial,
reducer,
middlewares: [sagaMiddleware],
});
React.useEffect(() => {
const task = sagaMiddleware.run(saga);
return () => task.cancel(); // cancel on unmount
}, [sagaMiddleware]);
return (
<Provider store={subStore}>
<p>
Any react-redux code below here in the React tree will use the subStore,
rather than the main store.
</p>
</Provider>
);
}Accessing the main store in a context that is using a subStore
If you need to access the main store in a React subtree that uses a subStore, try the following:
import { ParentProvider } from 'sub-redux';
function MyComponentInContextOfSubStore() {
return (
<>
<ThisComponentCanAccessTheSubStore />
<ParentProvider>
<ThisComponentCanAccessTheMainStore />
</ParentProvider>
</>
);
}ParentProvider accesses the subStore.parentStore, then renders a new
Provider with parent store - anything below that will use the parent store.