UNPKG

redux-composite

Version:
128 lines (106 loc) 4.51 kB
# Subscribe Store interface in `Redux` provides you `subscribe()` method. But there is only global `subscribe()`. What if you want to subscribe for low-level store changes? Let's say, we have 3 low-level states: - boolean `toggle` `state1` - array of 2 number states (`state2` and `state3`) And our task is to aggregate them in high-level state: `{toggle: state1, inc: [state2, state3]}`. Define first our low-level reducers and the high-level state structure: ```js const toggle = (state, action) => state === undefined ? false : (action.type === 'TOGGLE' ? !state : state); const inc = (state, action) => state === undefined ? 0 : (action.type === 'INCREMENT' ? state + 1 : state); let highLevelState = {toggle: false, inc: [1, 2]}; ``` According to design our composite structure would be: ```js const composite1 = Structure({ toggle, inc: [inc, inc] }); ``` Having the array of listeners, now we can define our high-level `dispatch()` method returned by original `createStore()` (like `Redux`): ```js let listeners = []; const highLevelDispatch1 = (reducer => action => { const newState = reducer(highLevelState, action); if (newState !== highLevelState) { highLevelState = newState; listeners.map(listener => listener()); } })(composite1.reducer); const createStore1 = () => ({ getState: () => highLevelState, dispatch: highLevelDispatch1, subscribe: listener => { listeners.push(listener); return () => listeners = listeners.filter(l => l !== listener); } }); ``` Ok, preconditions are set. And then after initializing the composite, we receive needed store `subscribe()` method for each low-level state: ```js composite1.createStore({createStore: createStore1})(); composite1.store.toggle.subscribe(({getState}) => { if (getState()) { composite1.store.inc[1].dispatch({type: 'INCREMENT'}); } }); ``` You can also subscribe from the top level to any low-level state change: ```js composite1.subscribe({ toggle: ({getState, dispatch}) => { if (getState()) { dispatch({type: 'TOGGLE'}); } } }); ``` So, what will happen if we dispatch `TOGGLE` action type: ```js composite1.store.toggle.dispatch({type: 'TOGGLE'}); // highLevelState is {toggle: false, inc: [1, 3]} ``` When we've dispatched `TOGGLE`, `state1` become `true` - and first subscriber triggered - and dispatched `INCREMENT` on `state3` of the `inc` array. That's how `state3` transformed from 2 to 3. And then second subscriber triggered, because `state1` is `true` - it dispatched the `TOGGLE` again. That's how `state1` become `false` again. What if our sub-states are already complex and have internal structure inside? For example: ```js highLevelState = {toggle: false, inc: [1, 2]}; const composite2 = Structure({ toggle, inc: Structure([inc, inc]) }); listeners = []; const highLevelDispatch2 = (reducer => action => { const newState = reducer(highLevelState, action); if (newState !== highLevelState) { highLevelState = newState; listeners.map(listener => listener()); } })(composite2.reducer); const createStore2 = () => ({ getState: () => highLevelState, dispatch: highLevelDispatch2, subscribe: listener => { listeners.push(listener); return () => listeners = listeners.filter(l => l !== listener); } }); ``` Notice, that `inc` is not simply an array anymore, but structure of the array. Than the `inc` property of store would be devided into `store` to access store methods (like `subscribe()`) and `structure` to access internal store of sub-structure: ```js composite2.createStore({createStore: createStore2})(); const unsubscribe = composite2.store.inc.store.subscribe(() => composite2.store.toggle.dispatch({type: 'TOGGLE'})); composite2.store.inc.structure[1].dispatch({type: 'INCREMENT'}); // highLevelState is {toggle: true, inc: [1, 3]} unsubscribe(); ``` When we've dispatched `INCREMENT` for the `state3`, `inc` state changed - that's why the listener was triggered. The listener dispatched `TOGGLE` - and the `state1` became `true`. And, as you can see, each `subscribe()` method returns `unsubscribe()` (if your original `subscribe()` method returns `unsubscribe()` in `createStore()` as in `Redux`) This way we have `subscribe()` methods for each sub-state... and even sub-sub-states and so on. Full example could be found in [examples/subscribe.js](../examples/subscribe.js) Read next: [Memoize](memoize.md)