All files / hooks/use-async/src types.ts

0% Statements 0/0
0% Branches 0/0
0% Functions 0/0
0% Lines 0/0

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131                                                                                                                                                                                                                                                                     
import type { AsyncState } from "@usefy/use-async-fn";
 
/**
 * The signature of an async function driven by {@link useAsync}.
 *
 * Unlike {@link import("@usefy/use-async-fn").AsyncFn}, an `AsyncFnWithSignal`
 * receives an {@link AbortSignal} as its **first** argument, followed by the
 * arguments forwarded to `execute(...)`. Wire the signal into your `fetch`
 * (or any abortable API) so a superseded / reset / unmounted request is
 * actually cancelled, not merely ignored:
 *
 * ```ts
 * const asyncFn = (signal: AbortSignal, id: string) =>
 *   fetch(`/api/user/${id}`, { signal }).then((r) => r.json());
 * ```
 *
 * The signal-first shape keeps the forwarded `Args` tuple clean and fully
 * inferable.
 *
 * @typeParam T - The resolved data type.
 * @typeParam Args - The tuple of arguments forwarded to the function (after the signal).
 */
export type AsyncFnWithSignal<T, Args extends unknown[] = unknown[]> = (
  signal: AbortSignal,
  ...args: Args
) => Promise<T>;
 
/**
 * The stable trigger returned by {@link useAsync} as `execute`. Call it from an
 * event handler; it forwards its arguments to the wrapped async function (after
 * the {@link AbortSignal}).
 *
 * Mirrors `useAsyncFn`'s `run`: the returned promise **never rejects** — errors
 * (including the abort of a superseded call) are surfaced via `state.error` or
 * discarded by the stale-guard. It resolves with the value `fn` produced for
 * *that specific call* on success (even if the call was later superseded —
 * supersession only suppresses the state/callback update, not this return
 * value), or `undefined` if `fn` rejected. (A `T` of `undefined` is therefore
 * ambiguous with failure — read `status`/`error` to disambiguate.)
 *
 * @typeParam T - The resolved data type.
 * @typeParam Args - The tuple of arguments forwarded to the function.
 */
export type AsyncExecuteFn<T, Args extends unknown[]> = (
  ...args: Args
) => Promise<T | undefined>;
 
/**
 * Options for {@link useAsync}.
 *
 * @typeParam T - The resolved data type.
 * @typeParam Args - The tuple of arguments forwarded to `fn`.
 * @typeParam E - The error type (defaults to `Error`).
 */
export interface UseAsyncOptions<
  T,
  Args extends unknown[] = unknown[],
  E = Error,
> {
  /**
   * Auto-run the task once on mount (client-side only, from an effect — never
   * during SSR render).
   *
   * **Defaults to `true`** — `useAsync` is the auto-running counterpart to the
   * manual `useAsyncFn`; the whole point of reaching for it (vs. `useAsyncFn`)
   * is a declarative "load on mount". Set `immediate: false` to defer to a
   * manual `execute()`.
   *
   * The immediate run uses {@link UseAsyncOptions.args} (or no args when
   * omitted). Under React 18 StrictMode the mount effect runs twice: the first
   * run's `AbortController` is aborted by the cleanup and the second run wins,
   * so state never ends up corrupted.
   */
  immediate?: boolean;
  /**
   * Arguments for the immediate (auto-run) invocation. Required if your async
   * function needs arguments and you keep `immediate` enabled — otherwise the
   * immediate run calls the function with no forwarded args. Ignored for manual
   * `execute(...)` calls (which take their own args).
   */
  args?: Args;
  /**
   * Seed value for `data` before the first successful run. The status still
   * starts as `"idle"` regardless of this value, and `reset()` restores it.
   */
  initialData?: T;
  /**
   * Called after a run resolves successfully — but only for the latest
   * (non-superseded) run and only while the component is still mounted. Fired
   * from the event turn (after `setState`), never from inside a state updater.
   */
  onSuccess?: (data: T) => void;
  /**
   * Called after a run fails — but only for the latest (non-superseded) run and
   * only while the component is still mounted. Fired from the event turn (after
   * `setState`), never from inside a state updater. The abort of a superseded /
   * reset / unmounted call is never reported here.
   */
  onError?: (error: E) => void;
}
 
/**
 * The object returned by {@link useAsync}: the full {@link AsyncState} spread
 * flat (`data` / `error` / `status` / `isLoading`) plus `execute` and `reset`.
 *
 * This is the object-style sibling of `useAsyncFn`'s `[state, run]` tuple —
 * the state fields carry identical meaning.
 *
 * @typeParam T - The resolved data type.
 * @typeParam Args - The tuple of arguments forwarded to `fn`.
 * @typeParam E - The error type (defaults to `Error`).
 */
export interface UseAsyncReturn<
  T,
  Args extends unknown[] = unknown[],
  E = Error,
> extends AsyncState<T, E> {
  /**
   * Runs the async function, forwarding `args` (after the `AbortSignal`).
   * Referentially stable. Aborts any previous in-flight request before starting.
   * Never rejects — see {@link AsyncExecuteFn}.
   */
  execute: AsyncExecuteFn<T, Args>;
  /**
   * Returns state to idle (`{ data: initialData, error: undefined, status: "idle" }`),
   * aborts any in-flight request, and supersedes it so its late settlement is
   * ignored. Referentially stable.
   */
  reset: () => void;
}