bowheart/zedux

View on GitHub
src/api/applyMachineHooks.ts

Summary

Maintainability
C
1 day
Test Coverage
A
100%
import {
  EffectsSubscriber,
  MachineHooksBuilder,
  Reactable,
  Store,
} from '../types'
import { extractActionType } from '../utils/actor'

type SubscribersHash = Record<string, Map<EffectsSubscriber, true>>

export const applyMachineHooks = <T = any>(
  store: Store<T>,
  getMachineState: (state: T) => string
) => {
  const enterSubscribers: SubscribersHash = {}
  const leaveSubscribers: SubscribersHash = {}

  const subscription = store.subscribe({
    effects: meta => {
      const { newState, oldState } = meta
      const newMachineState = getMachineState(newState)
      const oldMachineState = getMachineState(oldState)

      if (newMachineState === oldMachineState) return

      const leaveSubs = leaveSubscribers[oldMachineState]
      const enterSubs = enterSubscribers[newMachineState]

      if (leaveSubs) {
        ;[...leaveSubs.keys()].forEach(subscriber => subscriber(meta))
      }
      if (enterSubs) {
        ;[...enterSubs.keys()].forEach(subscriber => subscriber(meta))
      }
    },
  })

  const getSubscription = () => subscription

  const onEnter = (action: Reactable, subscriber: EffectsSubscriber) => {
    const actionType = extractActionType('withMachineHooks.onEnter')(action)

    if (!enterSubscribers[actionType]) {
      enterSubscribers[actionType] = new Map<EffectsSubscriber, true>()
    }

    enterSubscribers[actionType].set(subscriber, true)

    return builder // for chaining
  }

  const onLeave = (action: Reactable, subscriber: EffectsSubscriber) => {
    const actionType = extractActionType('withMachineHooks.onLeave')(action)

    if (!leaveSubscribers[actionType]) {
      leaveSubscribers[actionType] = new Map<EffectsSubscriber, true>()
    }

    leaveSubscribers[actionType].set(subscriber, true)

    return builder // for chaining
  }

  const builder: MachineHooksBuilder<T> = { getSubscription, onEnter, onLeave }

  return builder
}