DispatchingStore.ts 1.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. import { Subject, BehaviorSubject } from "rxjs"
  2. import { map } from "rxjs/operators"
  3. import assign from "lodash/assign"
  4. import clone from "lodash/clone"
  5. type dispatcherFunc<StoreType> = (
  6. currentVal: StoreType,
  7. payload: any
  8. ) => Partial<StoreType>
  9. /**
  10. * Defines a dispatcher.
  11. *
  12. * This function exists to provide better typing for dispatch function.
  13. * As you can see, its pretty much an identity function.
  14. */
  15. export const defineDispatchers = <StoreType, T>(
  16. // eslint-disable-next-line no-unused-vars
  17. dispatchers: { [_ in keyof T]: dispatcherFunc<StoreType> }
  18. ) => dispatchers
  19. type Dispatch<
  20. StoreType,
  21. DispatchersType extends Record<string, dispatcherFunc<StoreType>>
  22. > = {
  23. dispatcher: keyof DispatchersType
  24. payload: any
  25. }
  26. export default class DispatchingStore<
  27. StoreType,
  28. DispatchersType extends Record<string, dispatcherFunc<StoreType>>
  29. > {
  30. #state$: BehaviorSubject<StoreType>
  31. #dispatchers: DispatchersType
  32. #dispatches$: Subject<Dispatch<StoreType, DispatchersType>> = new Subject()
  33. constructor(initialValue: StoreType, dispatchers: DispatchersType) {
  34. this.#state$ = new BehaviorSubject(initialValue)
  35. this.#dispatchers = dispatchers
  36. this.#dispatches$
  37. .pipe(
  38. map(({ dispatcher, payload }) =>
  39. this.#dispatchers[dispatcher](this.value, payload)
  40. )
  41. )
  42. .subscribe((val) => {
  43. const data = clone(this.value)
  44. assign(data, val)
  45. this.#state$.next(data)
  46. })
  47. }
  48. get subject$() {
  49. return this.#state$
  50. }
  51. get value() {
  52. return this.subject$.value
  53. }
  54. get dispatches$() {
  55. return this.#dispatches$
  56. }
  57. dispatch({ dispatcher, payload }: Dispatch<StoreType, DispatchersType>) {
  58. if (!this.#dispatchers[dispatcher])
  59. throw new Error(`Undefined dispatch type '${dispatcher}'`)
  60. this.#dispatches$.next({ dispatcher, payload })
  61. }
  62. }