UNPKG

2.81 kBJavaScriptView Raw
1import { interpret } from 'xstate';
2import { atom } from 'jotai';
3
4const RESTART = Symbol();
5function atomWithMachine(getMachine, getOptions) {
6 const cachedMachineAtom = atom(null);
7 const machineAtom = atom((get) => {
8 const cachedMachine = get(cachedMachineAtom);
9 if (cachedMachine) {
10 return cachedMachine;
11 }
12 let initializing = true;
13 const safeGet = (a) => {
14 if (initializing) {
15 return get(a);
16 }
17 throw new Error("get not allowed after initialization");
18 };
19 const machine = isGetter(getMachine) ? getMachine(safeGet) : getMachine;
20 const options = isGetter(getOptions) ? getOptions(safeGet) : getOptions;
21 initializing = false;
22 const {
23 guards,
24 actions,
25 services,
26 delays,
27 context,
28 ...interpreterOptions
29 } = options || {};
30 const machineConfig = {
31 ...guards && { guards },
32 ...actions && { actions },
33 ...services && { services },
34 ...delays && { delays }
35 };
36 const machineWithConfig = machine.withConfig(machineConfig, () => ({
37 ...machine.context,
38 ...context
39 }));
40 const service = interpret(machineWithConfig, interpreterOptions);
41 return { machine: machineWithConfig, service };
42 }, (get, set, _arg) => {
43 set(cachedMachineAtom, get(machineAtom));
44 });
45 machineAtom.onMount = (commit) => {
46 commit();
47 };
48 const cachedMachineStateAtom = atom(null);
49 const machineStateAtom = atom((get) => {
50 var _a;
51 return (_a = get(cachedMachineStateAtom)) != null ? _a : get(machineAtom).machine.initialState;
52 }, (get, set, registerCleanup) => {
53 const { service } = get(machineAtom);
54 service.onTransition((nextState) => {
55 set(cachedMachineStateAtom, nextState);
56 });
57 service.start();
58 registerCleanup(() => {
59 const { service: service2 } = get(machineAtom);
60 service2.stop();
61 });
62 });
63 machineStateAtom.onMount = (initialize) => {
64 let unsub;
65 initialize((cleanup) => {
66 if (unsub === false) {
67 cleanup();
68 } else {
69 unsub = cleanup;
70 }
71 });
72 return () => {
73 if (unsub) {
74 unsub();
75 }
76 unsub = false;
77 };
78 };
79 const machineStateWithServiceAtom = atom((get) => get(machineStateAtom), (get, set, event) => {
80 const { service } = get(machineAtom);
81 if (event === RESTART) {
82 service.stop();
83 set(cachedMachineAtom, null);
84 set(machineAtom, null);
85 const { service: newService } = get(machineAtom);
86 newService.onTransition((nextState) => {
87 set(cachedMachineStateAtom, nextState);
88 });
89 newService.start();
90 } else {
91 service.send(event);
92 }
93 });
94 return machineStateWithServiceAtom;
95}
96const isGetter = (v) => typeof v === "function";
97
98export { RESTART, atomWithMachine };