UNPKG

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