@thebestco/store-manager

0.0.19 • Public • Published

BestPrice state management library

Getting started

Install

yarn add @thebestco/store-manager

Testing

yarn test

Documentation

makeResourceSubStore

Is going to be used for sub-stores like authentication, user

usage

All the parameters except the subStoreName are optional.

const userSubStore = makeResourceSubStore('user', {
  getInitialState,
  reducer,
  actions,
  sagas,
  actionTypes,
});

default initial state

{
  loading: true,
  loaded: false,
  processing: false,
  pristine: {},
  errors: {}
}

default actions

export const initialize = (values, { merge } = {}) => {};

export const update = (attr, value) => {};

export const updateBatch = values => {};

export const updateOnChange = ({ target }) => {};

export const reset = () => {};

export const startFetch = () => {};

export const doneFetch = payload => {};

export const failFetch = () => {};

export const startProcess = () => {};

export const doneProcess = () => {};

export const resetPristine = () => {};

export const resetPristineKey = attr => {};

makeCollectionSubStore

Should be used for sub-stores like products, images, etc. The sub store should contain pre-defined actions for both collection and collection resource handling.

usage

All the parameters except the subStoreName are optional.

const productsSubStore = makeCollectionSubStore('products', {
  getInitialState,
  reducer,
  actions,
  sagas,
  actionTypes,
  resourceOptions: {
    getInitialState,
  },
});

default initial state

{
  loading: true,
  loaded: false,
  processing: false,
  byId: {},
  ids: []
}

default resource initial state

{
  loading: true,
  loaded: false,
  processing: false,
  pristine: {},
  errors: {}
}

default actions

export const initialize = (values, { merge } = {}) => {};

export const update = (attr, value) => {};

export const updateBatch = values => {};

export const updateOnChange = ({ target }) => {};

export const reset = () => {};

export const startFetch = () => {};

export const doneFetch = payload => {};

export const failFetch = () => {};

export const startProcess = () => {};

export const doneProcess = () => {};

export const resetPristine = () => {};

export const resetPristineKey = attr => {};

// Resource

export const initializeResource = (id, values, { merge } = {}) => {};

export const updateResource = (id, attr, value) => {};

export const updateBatchResource = (id, values) => {};

export const updateOnChangeResource = (id, { target }) => {};

export const removeResource = id => {};

export const resetResource = id => {};

export const startFetchResource = id => {};

export const doneFetchResource = (id, payload) => {};

export const failFetchResource = id => {};

export const startProcessResource = id => {};

export const doneProcessResource = id => {};

export const resetPristineResource = id => {};

export const resetPristineKeyResource = (id, attr) => {};

makeStoreManager

const store = makeStoreManager([userSubStore, productSubStore]);

const {
  actions,
  actionTypes,
  sagas,
  subStoreNames,
  reducer,
  context,
  useDispatch,
  useSelector,
  useSubStoreSelector,
  useResourceSelector,
  useStore,
  addSubStores,
  removeSubStores,
  updateSagas,
  reset,
} = store;

actions.user.startProcess(); // {type: 'USER_START_PROCESS'}

actionTypes.user.startProcess; // 'USER_START_PROCESS'

const Foo = () => {
  const name = useSubStoreSelector('user', state => state.name);
  const dispatch = useDispatch();
  dispatch.user.stopProcess();
};

Code samples

Extending the sub stores

// products/provider/actionTypes.js
export default LAST_SEEN = 'PRODUCTS_LAST_SEEN';

// products/provider/actions.js
export const setLastSeen = id => ({
  type: actions.LAST_SEEN,
  id,
});

// products/provider/reducer.js
export const reducer = {
  [actions.LAST_SEEN]: (state, { id }) => ({ ...state, lastSeen: id }),
};

// products/provider/getInitialState.js
export default () => ({ lastSeen: null });

const subStore = makeCollectionSubStore('products', {
  getInitialState, // collection's initial state
  reducer,
  actions,
  resourceOptions: {
    getInitialState, // resource's initial state
    reducer, // resource's reducer
    actionTypes, // resource's action types
  },
});

Pristine state

The store tracks the fields that have changed since the last doneFetch/doneFetchResource. If there is a change a field, we should keep the pristine value in a pristine object.

  • The resetResource/reset actions should set the pristine values back to the state, and clear the pristine state
  • The initializeResource/initialize/doneFetchResource/doneFetch should reset the pristine state
  • The pristine updates should apply on the update/batchUpdate/updateResource/batchUpdateResource actions

Example

  1. [productID]: {loaded: true, loading: false, errors: {}, pristine: {}, name: 'Tyler', lastname: 'Durden}
  2. updateResource(id, 'name', 'Norton')
  3. [productID]: {loaded: true, loading: false, errors: {}, pristine: {name: 'Tyler'}, name: 'Norton', lastname: 'Durden}
  4. updateResource(id, 'name', 'Edward')
  5. [productID]: {loaded: true, loading: false, errors: {}, pristine: {name: 'Tyler'}, name: 'Edward', lastname: 'Durden}
  6. updateResource(id, 'name', 'Tyler')
  7. [productID]: {loaded: true, loading: false, errors: {}, pristine: {}, name: 'Tyler', lastname: 'Durden}

Create the root provider

const {
  actions,
  actionTypes,
  subStoreNames,
  sagas,
  reducer,
  context,
  useDispatch,
  useSelector,
  useSubStoreSelector,
  useResourceSelector,
  useStore,
  addSubStores,
  removeSubStores,
  updateSagas,
  reset,
} = makeStore([subStore]);

// re-export useDispatch, useSelector, useResourceSelector, context...

const makeReduxStore = () => {
  const sagaMiddleware = createSagaMiddleware();

  const store = createStore(
    reducer,
    {},
    composeWithDevTools({ name: "AppName" })(applyMiddleware(sagaMiddleware))
  );

  store.runSaga = sagaMiddleware.run;
  updateSagas(reduxStore.runSaga);

  return store;
};
const AppName = () => {
  const store = useMemo(() => makeReduxStore(), []);

  return (
    <Provider store={store} context={context}>
      <App {...props} />
    </Provider>
  );
};

Using the providers

import { useSelector, useDispatch } from '......provider';

const LastSeen = ({ id }) => {
  const loading = useSelector(state => state.products.loading);
  const dispatch = useDispatch();

  if (loading) {
    return <Loading />;
  }

  return <Button onClick={() => dispatch.products.setLastSeen(id)} />;
};

Add sub-stores dynamically

Add sub store and execute the sagas.

const subStores = [subStore1, subStore2, subStore3];

export default () => {
  const store = getStore();
  storeManager.addSubStores(store, store.runSaga, subStores);
};

Remove sub-stores dynamically

Remove given sub-stores.

// Substores is an array of instances.
const subStores = [subStore1, subStore2, subStore3];
// But can be also an array of strings of the substore names.
const subStoreNames = ['subStore1', 'subStore2', 'subStore3'];

export default () => {
  const store = getStore();
  storeManager.removeSubStores(store, subStores);

  // ... this also works
  storeManager.removeSubStores(store, subStoreNames);
};

Reset store

You can reset the store to it's initial state with the reset method.

// Suppose we have registered these stores dynamically in our app
const subStores = [subStore1, subStore2, subStore3];

// We can reset the store to it's initial state (will all the bootstrap sub-stores given)
export default () => {
  const store = getStore();
  storeManager.reset(store);
};

Dispatch

All substore actions

const dispatch = useDispatch();
dispatch.products.updateResource(id, 'rating', 5);

Substore actions

const dispatch = useDispatch('products');
dispatch.updateResource(id, 'rating', 5);

Selectors

useResourceSelector

Memoizes the selectors. Redux-store uses useSelector() with the default comparison strategy that is strict equality. The useResourceSelector most of the time selects a portion of the state. So, it works with shallowEqual strategy.

const rating = useResourceSelector('products', id, state => state.rating);

// does not rerender if `rating or loading` are not getting change
const {rating, state} = useResourceSelector('products', id, state => ({rating: state.rating, loading: state.loading});

useSubStoreSelector

Does not memoize, strict equality only

const ids = useSubStoreSelector('products', state => state.ids);

useSelector

Same as redux useSelector

const state = useSelector();
const loading = state.products.byId[id].loading;

Readme

Keywords

Package Sidebar

Install

npm i @thebestco/store-manager

Weekly Downloads

0

Version

0.0.19

License

ISC

Unpacked Size

924 kB

Total Files

116

Last publish

Collaborators

  • phaistonian