UNPKG

@dreamkit/solid

Version:

DreamKit tools for Solid.

111 lines (110 loc) 3.58 kB
import { useActionManager } from "./useActionManager.js"; import { batch, createSignal, onCleanup, onMount, untrack } from "solid-js"; export function createAction(cb) { const context = useActionManager(); const queue = new Map(); const asyncAction = async (args, abortController) => cb.bind({ abortController })(...args); const [id, setId] = createSignal(0); const [args, setArgs] = createSignal([]); const [result, setResult] = createSignal(); const [state, setState] = createSignal("idle"); const [usedError, setUsedError] = createSignal(false); const [error, setError] = createSignal(); const running = () => state() === "running"; const clear = () => set("idle", undefined, undefined); const set = (state, result, error) => { batch(() => { setResult(result); setError(error); setState(state); if (state === "idle") setArgs([]); }); }; const abort = () => { const idValue = untrack(id); const item = queue.get(idValue); if (item) { item.abortController.abort(); queue.delete(idValue); } clear(); }; const [lastArgs, setLastArgs] = createSignal(); const canRetry = () => !!lastArgs(); const retry = () => { const args = untrack(lastArgs); return action(...(args || [])); }; const action = (...args) => { if (untrack(running)) return; const selfId = untrack(id) + 1; const item = { abortController: new AbortController() }; const next = () => queue.delete(selfId); queue.set(selfId, item); batch(() => { setId(selfId); setArgs(args); setState("running"); }); setLastArgs(args); asyncAction(args, item.abortController) .then((result) => { if (!next()) return; set("success", result, undefined); }) .catch((error) => { if (!next()) return; console.error(error); set("error", undefined, error); }); }; Object.defineProperties(action, { ref: { value: { get errorWithoutUsing() { return error(); }, }, }, id: { get: id }, args: { get: args }, result: { get: result }, running: { get: running }, error: { get: () => { setUsedError(true); return error(); }, }, isErrorUsed: { get: usedError }, state: { get: state }, clear: { value: clear }, abort: { value: abort }, title: { get: () => cb.title }, params: { get: () => cb.params }, retry: { value: retry }, canRetry: { get: canRetry }, with: { value: (...args) => { return new Proxy(action, { get: (target, p) => target[p], apply: (target, self) => { const targetArgs = args.length === 1 && typeof args[0] === "function" ? [args[0]()] : args; target.bind(self)(...targetArgs); }, }); }, }, }); if (context) { onMount(() => context?.add(action)); onCleanup(() => context.remove(action)); } return action; }