@xstate/vue
Version:
XState tools for Vue
72 lines (66 loc) • 1.85 kB
JavaScript
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 };