UNPKG

@xstate/vue

Version:
72 lines (66 loc) 1.85 kB
import { onMounted, onBeforeUnmount, isRef, shallowRef, watch } from 'vue'; import { createActor, toObserver } from 'xstate'; function useActorRef(actorLogic, ...[options, observerOrListener]) { const actorRef = createActor(actorLogic, options); let sub; onMounted(() => { if (observerOrListener) { sub = actorRef.subscribe(toObserver(observerOrListener)); } actorRef.start(); }); onBeforeUnmount(() => { actorRef.stop(); sub?.unsubscribe(); }); return actorRef; } function defaultCompare(a, b) { return a === b; } const noop = () => { /* ... */ }; function useSelector(actor, selector, compare = defaultCompare) { const actorRefRef = isRef(actor) ? actor : shallowRef(actor); const selected = shallowRef(selector(actorRefRef.value?.getSnapshot())); const updateSelectedIfChanged = nextSelected => { if (!compare(selected.value, nextSelected)) { selected.value = nextSelected; } }; watch(actorRefRef, (newActor, _, onCleanup) => { selected.value = selector(newActor?.getSnapshot()); if (!newActor) { return; } const sub = newActor.subscribe({ next: emitted => { updateSelectedIfChanged(selector(emitted)); }, error: noop, complete: noop }); onCleanup(() => sub.unsubscribe()); }, { immediate: true }); return selected; } function useActor(actorLogic, options = {}) { function listener(nextSnapshot) { snapshot.value = nextSnapshot; } const actorRef = useActorRef(actorLogic, options, listener); const snapshot = useSelector(actorRef, s => s); return { snapshot, send: actorRef.send, actorRef: actorRef }; } /** @alias useActor */ function useMachine(machine, ...[options]) { return useActor(machine, options); } export { useActor, useActorRef, useMachine, useSelector };