[]previous and next page

useResource

Loads data asynchronously and exposes it reactively.

Loads data asynchronously and exposes it reactively. Reading it while the first load is pending holds the nearest <Suspense> boundary around the reader (not the creator) in its fallback; reading it after a failure throws the error to the nearest error boundary. Refetching keeps the previous value visible (state === "refreshing") and never re-suspends.

Two forms: useResource(fetcher) fetches once; useResource(source, fetcher) refetches whenever source() changes and stays unresolved while it is false, null or undefined.

T is inferred from what the fetcher returns. info.value is typed unknown unless the parameter is annotated: (info: ResourceInfo<User>) => ....

Source form of useResource: fetches for each value of source, and refetches whenever it changes. While source() is false, null or undefined nothing is fetched and the resource stays "unresolved" (or keeps its value, "ready"). A newer fetch aborts the previous one, whose late result is ignored.

Import#

ts
import { useResource } from "@shadow-js/core";

Signature#

ts
function useResource<T>(
  fetcher: (info: ResourceInfo) => T | Promise<T>,
  options?: ResourceOptions<T>,
): Resource<T>

function useResource<S, T>(
  source: Accessor<S | false | null | undefined>,
  fetcher: (source: S, info: ResourceInfo) => T | Promise<T>,
  options?: ResourceOptions<T>,
): Resource<T>

function useResource<S extends string | number | boolean | bigint | object, T>(
  source: S,
  fetcher: (source: S, info: ResourceInfo) => T | Promise<T>,
  options?: ResourceOptions<T>,
): Resource<T>

function useResource<T>(
  fetcher: (info: ResourceInfo<T>) => T | Promise<T>,
  options?: ResourceOptions<T>,
): Resource<T>

function useResource<S, T>(
  source: Accessor<S | false | null | undefined>,
  fetcher: (source: S, info: ResourceInfo<T>) => T | Promise<T>,
  options?: ResourceOptions<T>,
): Resource<T>

Parameters#

Name Description
fetcher Called untracked, once at creation (skipped when a server render already provided the value) and again for every refetch(). It receives an abort signal, the refetching flag and the last value; it returns the value or a promise.
options Initial value, server key and a devtools name. Undefined uses the defaults.
source Tracked; its value is passed to fetcher. A plain value fetches once.
fetcher Called untracked with the source value and the fetch info.
options Initial value, server key and a devtools name. Undefined uses the defaults.

Returns#

The resource (see Resource).

Throws#

  • Nothing itself: a fetcher that throws or rejects makes the resource "errored". Reading the resource then rethrows that error, so the nearest error boundary receives it.
  • Nothing itself. Reading the resource rethrows the error of a failed fetch.

Examples#

ts
import { useResource } from "@shadow-js/core";

function Clock() {
  const time = useResource(({ signal }) => fetch("/api/time", { signal }).then((r) => r.text()));
  return <p>{time.loading ? "Loading..." : time()}</p>;
}
ts
import { useResource, useSignal } from "@shadow-js/core";

function UserCard() {
  const [id] = useSignal(1);
  const user = useResource(id, (id, { signal }) =>
    fetch(`/api/users/${id}`, { signal }).then((r) => r.json() as Promise<{ name: string }>),
  );
  return <p>{user()?.name}</p>;
}