[]previous and next page

Real fetch

Real fetch in the Data fetching guide.

For agentsUpdated

The fetcher is ordinary async code. Return the parsed value, throw on a bad status, and pass the signal:

src/http.tsxtsx
import { useResource, useStore } from "@shadow-js/core";

export interface Post {
  readonly id: number;
  readonly title: string;
}

async function getJson<T>(url: string, signal: AbortSignal): Promise<T> {
  const response = await fetch(url, { signal });
  if (!response.ok) throw new Error(`${response.status} ${response.statusText}`);
  return (await response.json()) as T;
}

export function PostView() {
  const [id, setId] = useStore(1);
  const post = useResource(id, (value, { signal }) =>
    getJson<Post>(`/api/posts/${value}`, signal),
  );
  return (
    <article>
      <button type="button" onClick={() => setId((current) => current + 1)}>
        next
      </button>
      <h1>{post.latest?.title}</h1>
      <p>{post.loading ? "loading" : post.error === undefined ? "" : String(post.error)}</p>
    </article>
  );
}

response.json() is typed unknown-like data from the network: validate it (a schema library, or hand-written checks) before trusting the as T cast, especially for data you render with innerHTML or use to build URLs. When the request is aborted, fetch rejects with an AbortError; the resource ignores the result of an aborted request, so the error never reaches the UI.