Bext Query (signaux)

Cache de données réactif sur les signaux bext — API façon TanStack (createQuery, createMutation, invalidateQueries, dehydrate/hydrate) sans React. La page pré-charge /api/poll côté serveur, déshydrate le cache dans le marqueur d'îlot, et l'îlot hydrate son cache client avant le premier appel à createQuery — ainsi le premier rendu affiche de vraies données, sans flash de chargement.

Astuce
La déshydratation du cache côté serveur (client.dehydrate()) est sérialisée dans le marqueur d'îlot et transmise au client dans le HTML initial — aucune requête supplémentaire n'est nécessaire au montage. Utilisez staleTime pour contrôler quand le client re-fetche.

Résultat



Manual query (staleTime 5s)

status: success · fetching: no · stale: no

now =

Live query (refetchInterval 3s)

tick =

src/components/QueryDemo.tsxTSX
"use signals";
/** @jsxImportSource @bext-stack/framework/signals */

import {
  createQueryClient, createQuery, createMutation,
  type DehydratedState,
} from "@bext-stack/framework/query";

export default function QueryDemo(props: { dehydrate?: DehydratedState }) {
  const client = createQueryClient();
  client.hydrate(props.dehydrate);

  const poll = createQuery<{ now: string }>(client, {
    queryKey: ["poll"],
    queryFn: async ({ signal }) => (await fetch("/api/poll", { signal })).json(),
    staleTime: 5_000,
  });

  return (
    <div>
      <p>status: {poll.status.value} · fetching: {poll.isFetching.value ? "yes" : "no"}</p>
      <p>now = {poll.data.value?.now ?? "—"}</p>
      <button onClick={() => poll.refetch()}>refetch</button>
    </div>
  );
}
src/app/examples/query/page.tsxTSX
import { signalsIsland } from "@bext-stack/framework/signals";
import { createQueryClient } from "@bext-stack/framework/query";
import QueryDemo from "../../../components/QueryDemo";

export default async function Page() {
  const client = createQueryClient();
  // Server-side prefetch — populates the cache before the island renders.
  await client.prefetchQuery({
    queryKey: ["poll"],
    queryFn: async () =>
      (await fetch("https://demo.bext.dev/api/poll")).json(),
  });
  return signalsIsland("QueryDemo", QueryDemo, {
    dehydrate: client.dehydrate(),
  });
}