deno.land / x / jotai@v1.8.4 / src / xstate / atomWithMachine.ts
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186import { interpret } from 'xstate'import type { AnyInterpreter, AnyStateMachine, AreAllImplementationsAssumedToBeProvided, EventObject, InternalMachineOptions, InterpreterFrom, InterpreterOptions, Prop, StateConfig, StateFrom,} from 'xstate'import type { Atom, Getter, WritableAtom } from 'jotai'import { atom } from 'jotai'
export const RESTART = Symbol()
export interface MachineAtomOptions<TContext, TEvent extends EventObject> { /** * If provided, will be merged with machine's `context`. */ context?: Partial<TContext> /** * The state to rehydrate the machine to. The machine will * start at this state instead of its `initialState`. */ state?: StateConfig<TContext, TEvent>}
type Options<TMachine extends AnyStateMachine> = AreAllImplementationsAssumedToBeProvided< TMachine['__TResolvedTypesMeta'] > extends false ? InterpreterOptions & MachineAtomOptions<TMachine['__TContext'], TMachine['__TEvent']> & InternalMachineOptions< TMachine['__TContext'], TMachine['__TEvent'], TMachine['__TResolvedTypesMeta'], true > : InterpreterOptions & MachineAtomOptions<TMachine['__TContext'], TMachine['__TEvent']> & InternalMachineOptions< TMachine['__TContext'], TMachine['__TEvent'], TMachine['__TResolvedTypesMeta'] >
type MaybeParam<T> = T extends (v: infer V) => unknown ? V : never
export function atomWithMachine< TMachine extends AnyStateMachine, TInterpreter = InterpreterFrom<TMachine>>( getMachine: TMachine | ((get: Getter) => TMachine), getOptions?: Options<TMachine> | ((get: Getter) => Options<TMachine>)): WritableAtom< StateFrom<TMachine>, MaybeParam<Prop<TInterpreter, 'send'>> | typeof RESTART, void> { const cachedMachineAtom = atom<{ machine: AnyStateMachine service: AnyInterpreter } | null>(null) const machineAtom = atom( (get) => { const cachedMachine = get(cachedMachineAtom) if (cachedMachine) { return cachedMachine } let initializing = true const safeGet = (a: Atom<unknown>) => { if (initializing) { return get(a) } throw new Error('get not allowed after initialization') } const machine = isGetter(getMachine) ? getMachine(safeGet) : getMachine const options = isGetter(getOptions) ? getOptions(safeGet) : getOptions initializing = false const { guards, actions, services, delays, context, ...interpreterOptions } = options || {}
const machineConfig = { ...(guards && { guards }), ...(actions && { actions }), ...(services && { services }), ...(delays && { delays }), }
const machineWithConfig = machine.withConfig( machineConfig as any, () => ({ ...machine.context, ...context, }) )
const service = interpret(machineWithConfig, interpreterOptions) return { machine: machineWithConfig, service } }, (get, set, _arg) => { set(cachedMachineAtom, get(machineAtom)) } )
machineAtom.onMount = (commit) => { commit() }
const cachedMachineStateAtom = atom<StateFrom<TMachine> | null>(null)
const machineStateAtom = atom( (get) => get(cachedMachineStateAtom) ?? (get(machineAtom).machine.initialState as StateFrom<TMachine>), (get, set, registerCleanup: (cleanup: () => void) => void) => { const { service } = get(machineAtom) service.onTransition((nextState: any) => { set(cachedMachineStateAtom, nextState) }) service.start() registerCleanup(() => { const { service } = get(machineAtom) service.stop() }) } )
machineStateAtom.onMount = (initialize) => { let unsub: (() => void) | undefined | false
initialize((cleanup) => { if (unsub === false) { cleanup() } else { unsub = cleanup } })
return () => { if (unsub) { unsub() } unsub = false } }
const machineStateWithServiceAtom = atom( (get) => get(machineStateAtom), ( get, set, event: Parameters<AnyInterpreter['send']>[0] | typeof RESTART ) => { const { service } = get(machineAtom) if (event === RESTART) { service.stop() set(cachedMachineAtom, null) set(machineAtom, null) const { service: newService } = get(machineAtom) newService.onTransition((nextState: any) => { set(cachedMachineStateAtom, nextState) }) newService.start() } else { service.send(event) } } )
return machineStateWithServiceAtom}
const isGetter = <T>(v: T | ((get: Getter) => T)): v is (get: Getter) => T => typeof v === 'function'
Version Info