Skip to content

adam1658/sub-redux

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SubRedux

npm version npm

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

or

$ yarn add sub-redux

Usage 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].state and subRedux[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.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

No packages published