All files / src/primitives resource.ts

100% Statements 32/32
100% Branches 8/8
100% Functions 2/2
100% Lines 32/32

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 551x 1x 1x                         1x 4x 4x 4x 4x 4x 4x 4x   4x 6x 6x 6x   6x 6x 6x 5x 6x 1x 6x 6x 6x 6x   6x 4x     4x       4x   4x   4x 4x  
import { createSignal } from './signal';
import { createEffect } from './effect';
import { createMemo } from './memo';
import type { Resource, SignalGetter, ReactiveOptions } from '../types';
 
type Fetcher<S, T> = (source: S) => T | Promise<T>;
 
/**
 * Creates a resource that handles asynchronous data fetching.
 * It automatically re-fetches when its source signal changes.
 *
 * @param source A signal that provides the input for the fetcher.
 * @param fetcher A function that takes the source value and returns the data or a Promise.
 * @param options Optional configuration, including a debug name.
 */
export function createResource<S, T, E = unknown>(
  source: SignalGetter<S>,
  fetcher: Fetcher<S, T>,
  options?: ReactiveOptions
): Resource<T, E> {
  const [data, setData] = createSignal<T | undefined>(undefined, options);
  const [loading, setLoading] = createSignal<boolean>(true);
  const [error, setError] = createSignal<E | undefined>(undefined);
 
  createEffect(() => {
    const sourceValue = source();
    setLoading(true);
    setError(undefined);
 
    const executeFetch = async () => {
      try {
        const result = await fetcher(sourceValue);
        setData(result);
      } catch (e) {
        setError(e as E);
      } finally {
        setLoading(false);
      }
    };
 
    executeFetch();
  }, options);
 
  // The main return value is a memo of the data
  const resourceMemo = createMemo(() => data(), options);
 
  // Attach the loading and error states as memos
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  (resourceMemo as any).loading = createMemo(() => loading());
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  (resourceMemo as any).error = createMemo(() => error());
 
  return resourceMemo as Resource<T, E>;
}