React

Install the adapter alongside React 18.3 or newer:

pnpm add @omni-async/react

Query

import { useQuery } from "@omni-async/react";

function Search() {
  const results = useQuery(
    async (term: string) => {
      const response = await fetch(`/api/search?q=${encodeURIComponent(term)}`);
      if (!response.ok) throw new Error("Search failed");
      return response.json() as Promise<string[]>;
    },
    { initial: () => [] },
  );

  return (
    <>
      <button disabled={results.loading} onClick={() => void results.trigger("async")}>
        Search
      </button>
      {results.error ? <p>Search failed</p> : null}
      <ul>
        {results.data.map((item) => (
          <li key={item}>{item}</li>
        ))}
      </ul>
    </>
  );
}

useQuery uses latest-request concurrency. Its initial function supplies initial data and is called again to produce fallback data after an error.

Fetch on mount

const users = useFetch(
  async (signal) => {
    const response = await fetch("/api/users", { signal });
    return response.json() as Promise<User[]>;
  },
  { initial: () => [] },
);

useFetch starts on mount, aborts the previous fetch before a new one, and aborts during cleanup.