All files create-store.ts

100% Statements 34/34
100% Branches 8/8
100% Functions 9/9
100% Lines 32/32

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 851x 1x 1x                     1x   3x   2x 6x       2x 2x 4x       2x 6x 6x 6x   9x         2x   2x   9x             2x   4x     2x   2x 1x 4x 4x   4x 2x 2x 1x   1x             2x   2x                  
import { Observable, merge, Subject, BehaviorSubject } from 'rxjs';
import { map, scan, distinctUntilChanged } from 'rxjs/operators';
import { AnyAction, wrapDispatchSubject } from './utils';
 
type GenericInputT = Observable<any> | Subject<any>;
type StateRxInputT = {
  name: string;
  _dispatchers$: Subject<Subject<AnyAction<any>>>;
  state$: Subject<any>;
  action$: Observable<any>;
};
type ObservableInputT = GenericInputT | StateRxInputT;
 
export const createStore = (
  observables: { [key: string]: ObservableInputT },
  initialState = {}
) => {
  const stateRxBranches = Object.values(observables).filter(
    (branch) => 'state$' in branch
  ) as StateRxInputT[];
 
  // Create dispatch function and add to all of our branches
  const dispatch$ = new Subject<AnyAction<any>>();
  stateRxBranches.forEach((branch) => {
    branch._dispatchers$.next(dispatch$);
  });
 
  // Create Normalized State {[stateKey]: observable}
  const normalizedObservables = Object.entries(observables).map(
    ([key, input]) => {
      const obs = 'state$' in input ? input.state$ : input;
      return obs.pipe(
        distinctUntilChanged(),
        map((val) => ({ [key]: val }))
      );
    }
  );
 
  const state$ = new BehaviorSubject(initialState);
 
  const reducer$ = merge(...normalizedObservables).pipe(
    scan((acc, val) => {
      return {
        ...acc,
        ...val
      };
    }, initialState)
  );
 
  const action$ = merge(
    dispatch$,
    ...stateRxBranches.map((state) => state.action$)
  ).pipe(distinctUntilChanged());
 
  reducer$.subscribe(state$);
 
  const setState = (newState: { [key: string]: any }) => {
    for (const key in newState) {
      const obs = observables[key];
      const newValue = newState[key];
 
      if ('state$' in obs) {
        obs.state$.next(newValue);
      } else if ('next' in obs) {
        obs.next(newValue);
      } else {
        console.warn(
          `Cannot set store value of ${key}. It is not a StateRx Objects or subject.`
        );
      }
    }
  };
 
  const getState = () => state$.getValue();
 
  return {
    state$,
    action$,
    dispatch: wrapDispatchSubject(dispatch$),
    initialState,
    setState,
    getState
  };
};