@seahax/elemental
Advanced tools
| import { type ReadonlyRef, type RefValues } from './useRef.ts'; | ||
| export interface AsyncValue<TValue> { | ||
| export type AsyncValue<TValue> = { | ||
| readonly state: 'loading'; | ||
| readonly value: TValue | undefined; | ||
| readonly error: unknown; | ||
| readonly isLoading: boolean; | ||
| } | ||
| } | { | ||
| readonly state: 'success'; | ||
| readonly value: TValue; | ||
| readonly error: undefined; | ||
| } | { | ||
| readonly state: 'error'; | ||
| readonly value: TValue | undefined; | ||
| readonly error: unknown; | ||
| }; | ||
| export interface AsyncOptions { | ||
@@ -8,0 +16,0 @@ readonly debounceMs?: number; |
@@ -7,5 +7,5 @@ import { useRef as e } from "./useRef.js"; | ||
| let o = e({ | ||
| state: "loading", | ||
| value: void 0, | ||
| error: void 0, | ||
| isLoading: !0 | ||
| error: void 0 | ||
| }), s = !0; | ||
@@ -16,9 +16,9 @@ return n(r, (...e) => { | ||
| if (t.signal.aborted) return; | ||
| o.value.isLoading || (o.value = { | ||
| o.value.state !== "loading" && (o.value = { | ||
| ...o.value, | ||
| isLoading: !0 | ||
| state: "loading" | ||
| }); | ||
| let n = await i(t.signal, ...e); | ||
| t.signal.aborted || (o.value = { | ||
| isLoading: !1, | ||
| state: "success", | ||
| value: n, | ||
@@ -29,4 +29,4 @@ error: void 0 | ||
| t.signal.aborted || (o.value = { | ||
| isLoading: !1, | ||
| value: void 0, | ||
| ...o.value, | ||
| state: "error", | ||
| error: e | ||
@@ -33,0 +33,0 @@ }); |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"useAsync.js","names":[],"sources":["../../src/hooks/useAsync.ts"],"sourcesContent":["import { useDisconnectEffect } from './useDisconnectEffect.ts';\nimport { useEffect } from './useEffect.ts';\nimport { type ReadonlyRef, type RefValues, useRef } from './useRef.ts';\n\nexport interface AsyncValue<TValue> {\n readonly value: TValue | undefined;\n readonly error: unknown;\n readonly isLoading: boolean;\n}\n\nexport interface AsyncOptions {\n readonly debounceMs?: number;\n}\n\n/** Use a reference (reactive state) bound to an async loader function. */\nexport function useAsync<const TDeps extends readonly ReadonlyRef<any>[], TValue>(\n deps: TDeps,\n callback: (signal: AbortSignal, ...values: RefValues<TDeps>) => Promise<TValue>,\n { debounceMs }: AsyncOptions = {},\n): ReadonlyRef<AsyncValue<TValue>> {\n const ref = useRef<AsyncValue<TValue>>({ value: undefined, error: undefined, isLoading: true });\n let skipDebounce = true;\n\n useEffect(deps, (...values) => {\n const ac = new AbortController();\n\n Promise.race(\n skipDebounce\n ? [Promise.resolve()]\n : [\n new Promise((resolve) => setTimeout(resolve, debounceMs)),\n new Promise((resolve) => ac.signal.addEventListener('abort', resolve, { once: true })),\n ],\n )\n .then(async () => {\n if (ac.signal.aborted) return;\n if (!ref.value.isLoading) ref.value = { ...ref.value, isLoading: true };\n const value = await callback(ac.signal, ...(values as any));\n if (ac.signal.aborted) return;\n ref.value = { isLoading: false, value, error: undefined };\n })\n .catch((error: unknown) => {\n if (ac.signal.aborted) return;\n ref.value = { isLoading: false, value: undefined, error };\n });\n\n skipDebounce = false;\n return () => ac.abort();\n });\n\n useDisconnectEffect(() => {\n // Skip the debounce again after the component disconnects.\n skipDebounce = true;\n });\n\n return ref;\n}\n"],"mappings":";;;;AAeA,SAAgB,EACd,GACA,GACA,EAAE,kBAA6B,EAAE,EACA;CACjC,IAAM,IAAM,EAA2B;EAAE,OAAO,KAAA;EAAW,OAAO,KAAA;EAAW,WAAW;EAAM,CAAC,EAC3F,IAAe;AAkCnB,QAhCA,EAAU,IAAO,GAAG,MAAW;EAC7B,IAAM,IAAK,IAAI,iBAAiB;AAuBhC,SArBA,QAAQ,KACN,IACI,CAAC,QAAQ,SAAS,CAAC,GACnB,CACE,IAAI,SAAS,MAAY,WAAW,GAAS,EAAW,CAAC,EACzD,IAAI,SAAS,MAAY,EAAG,OAAO,iBAAiB,SAAS,GAAS,EAAE,MAAM,IAAM,CAAC,CAAC,CACvF,CACN,CACE,KAAK,YAAY;AAChB,OAAI,EAAG,OAAO,QAAS;AACvB,GAAK,EAAI,MAAM,cAAW,EAAI,QAAQ;IAAE,GAAG,EAAI;IAAO,WAAW;IAAM;GACvE,IAAM,IAAQ,MAAM,EAAS,EAAG,QAAQ,GAAI,EAAe;AACvD,KAAG,OAAO,YACd,EAAI,QAAQ;IAAE,WAAW;IAAO;IAAO,OAAO,KAAA;IAAW;IACzD,CACD,OAAO,MAAmB;AACrB,KAAG,OAAO,YACd,EAAI,QAAQ;IAAE,WAAW;IAAO,OAAO,KAAA;IAAW;IAAO;IACzD,EAEJ,IAAe,UACF,EAAG,OAAO;GACvB,EAEF,QAA0B;AAExB,MAAe;GACf,EAEK"} | ||
| {"version":3,"file":"useAsync.js","names":[],"sources":["../../src/hooks/useAsync.ts"],"sourcesContent":["import { useDisconnectEffect } from './useDisconnectEffect.ts';\nimport { useEffect } from './useEffect.ts';\nimport { type ReadonlyRef, type RefValues, useRef } from './useRef.ts';\n\nexport type AsyncValue<TValue> =\n | { readonly state: 'loading'; readonly value: TValue | undefined; readonly error: unknown }\n | { readonly state: 'success'; readonly value: TValue; readonly error: undefined }\n | { readonly state: 'error'; readonly value: TValue | undefined; readonly error: unknown };\n\nexport interface AsyncOptions {\n readonly debounceMs?: number;\n}\n\n/** Use a reference (reactive state) bound to an async loader function. */\nexport function useAsync<const TDeps extends readonly ReadonlyRef<any>[], TValue>(\n deps: TDeps,\n callback: (signal: AbortSignal, ...values: RefValues<TDeps>) => Promise<TValue>,\n { debounceMs }: AsyncOptions = {},\n): ReadonlyRef<AsyncValue<TValue>> {\n const ref = useRef<AsyncValue<TValue>>({ state: 'loading', value: undefined, error: undefined });\n let skipDebounce = true;\n\n useEffect(deps, (...values) => {\n const ac = new AbortController();\n\n Promise.race(\n skipDebounce\n ? [Promise.resolve()]\n : [\n new Promise((resolve) => setTimeout(resolve, debounceMs)),\n new Promise((resolve) => ac.signal.addEventListener('abort', resolve, { once: true })),\n ],\n )\n .then(async () => {\n if (ac.signal.aborted) return;\n if (ref.value.state !== 'loading') ref.value = { ...ref.value, state: 'loading' };\n const value = await callback(ac.signal, ...(values as any));\n if (ac.signal.aborted) return;\n ref.value = { state: 'success', value, error: undefined };\n })\n .catch((error: unknown) => {\n if (ac.signal.aborted) return;\n ref.value = { ...ref.value, state: 'error', error };\n });\n\n skipDebounce = false;\n return () => ac.abort();\n });\n\n useDisconnectEffect(() => {\n // Skip the debounce again after the component disconnects.\n skipDebounce = true;\n });\n\n return ref;\n}\n"],"mappings":";;;;AAcA,SAAgB,EACd,GACA,GACA,EAAE,kBAA6B,EAAE,EACA;CACjC,IAAM,IAAM,EAA2B;EAAE,OAAO;EAAW,OAAO,KAAA;EAAW,OAAO,KAAA;EAAW,CAAC,EAC5F,IAAe;AAkCnB,QAhCA,EAAU,IAAO,GAAG,MAAW;EAC7B,IAAM,IAAK,IAAI,iBAAiB;AAuBhC,SArBA,QAAQ,KACN,IACI,CAAC,QAAQ,SAAS,CAAC,GACnB,CACE,IAAI,SAAS,MAAY,WAAW,GAAS,EAAW,CAAC,EACzD,IAAI,SAAS,MAAY,EAAG,OAAO,iBAAiB,SAAS,GAAS,EAAE,MAAM,IAAM,CAAC,CAAC,CACvF,CACN,CACE,KAAK,YAAY;AAChB,OAAI,EAAG,OAAO,QAAS;AACvB,GAAI,EAAI,MAAM,UAAU,cAAW,EAAI,QAAQ;IAAE,GAAG,EAAI;IAAO,OAAO;IAAW;GACjF,IAAM,IAAQ,MAAM,EAAS,EAAG,QAAQ,GAAI,EAAe;AACvD,KAAG,OAAO,YACd,EAAI,QAAQ;IAAE,OAAO;IAAW;IAAO,OAAO,KAAA;IAAW;IACzD,CACD,OAAO,MAAmB;AACrB,KAAG,OAAO,YACd,EAAI,QAAQ;IAAE,GAAG,EAAI;IAAO,OAAO;IAAS;IAAO;IACnD,EAEJ,IAAe,UACF,EAAG,OAAO;GACvB,EAEF,QAA0B;AAExB,MAAe;GACf,EAEK"} |
+1
-1
@@ -28,3 +28,3 @@ { | ||
| }, | ||
| "version": "0.8.4" | ||
| "version": "0.8.5" | ||
| } |
113008
0.35%1008
0.8%