All files / src Store.ts

89.74% Statements 35/39
87.5% Branches 21/24
90% Functions 9/10
89.74% Lines 35/39

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 85 86 87 88 89 90 91 92 93 94                24x 24x 24x 24x 24x 24x 24x         24x 20x           20x                     26x 26x 26x 26x       3x 3x 1x 1x 1x         71x     71x 52x 52x 22x 22x 22x 29x     22x 22x                 71x 28x 11x         43x         22x 22x          
import { Reducer, ActionLike, Action, Deps } from './types';
import { Epic } from './Epic';
import { Notify } from './Notify';
import { logAction } from './utils';
 
type Listener = () => void;
 
export class Store<TState = any> {
  public isEnabled: boolean = false;
  public state: TState | undefined = undefined;
  public reducer: Reducer<TState> | null = null;
  public epic: Epic | null = null;
  private listeners: Listener[] = [];
  private usageCount: number = 0;
  private isStateInited = false;
 
  constructor(public name: symbol, public displayName: string) {}
 
  initState() {
    if (this.reducer && !this.isStateInited) {
      this.state = this.reducer(undefined, {
        type: [Symbol('__INIT__'), 'init'],
      });
      // don't reset state if the module is remounted
      // for example when navigating from PageA -> PageB -> PageA
      // PageA should reset its state manually
      this.isStateInited = true;
    }
  }
 
  enable({
    epic,
    reducer,
  }: {
    epic: Epic | null;
    reducer: Reducer<TState> | null;
  }) {
    this.usageCount++;
    this.epic = epic || null;
    this.reducer = reducer || null;
    this.isEnabled = true;
  }
 
  disable() {
    this.usageCount--;
    if (!this.usageCount) {
      this.epic = null;
      this.reducer = null;
      this.isEnabled = false;
    }
  }
 
  dispatch(action: ActionLike, notify?: Notify) {
    Iif (!this.isEnabled) {
      return;
    }
    if (this.reducer) {
      const nextState = this.reducer(this.state, action);
      if (nextState !== this.state) {
        this.state = nextState;
        const notifyFn = () => {
          for (const listener of this.listeners) {
            listener();
          }
        };
        Eif (notify) {
          notify.add(notifyFn);
        } else {
          notifyFn();
        }
      }
    }
  }
 
  getOutputStream(action: Action, deps: Deps) {
    if (this.isEnabled && this.epic != null) {
      return this.epic.toStream(action, deps, () => {
        Iif (process.env.NODE_ENV === 'development') {
          logAction(this.displayName, action);
        }
      });
    } else {
      return null;
    }
  }
 
  subscribe(listener: Listener) {
    this.listeners.push(listener);
    return () => {
      this.listeners.splice(this.listeners.indexOf(listener), 1);
    };
  }
}