Data fetching
Load, cache, retry, refresh, and mutate remote data with useActivityQuery.
useActivityQuery() loads asynchronous data into a shared browser cache. It deduplicates work by key, retries transient failures, aborts timed-out requests, keeps old data during refresh, and exposes explicit refetch and mutation controls.
#Create a query
1import { useActivityQuery } from "ludicord/activity";23interface Profile {4 name: string;5 wins: number;6}78const profile = useActivityQuery<Profile>(9 ["profile", userId],10 async ({ signal }) => {11 const response = await fetch(`/api/players/${userId}`, { signal });12 if (!response.ok) throw new Error("Profile request failed");13 return response.json() as Promise<Profile>;14 },15 { enabled: Boolean(userId), staleTime: 30_000, retries: 2 },16);
Query keys may be non-empty strings or JSON-serializable arrays. Every component using the same serialized key shares the same cached snapshot and in-flight request.
#Render every status
1if (profile.status === "loading") return <ProfileSkeleton />;2if (profile.status === "error") return <Retry error={profile.error} onRetry={profile.refetch} />;3if (!profile.data) return null;4return <ProfileCard profile={profile.data} refreshing={profile.status === "refreshing"} />;
#Configure behavior
Retries use a short exponential delay capped at two seconds. The query callback receives an AbortSignal; pass it through so replacement, timeout, and cache clearing can cancel useful work.
#Refetch and mutate
refetch() forces a new request and returns its promise. mutate() updates the cache immediately without contacting the server.
1await saveProfile(next);2profile.mutate((current) => current ? { ...current, name: next.name } : next);
Optimistic mutation is not a security boundary. The API route still validates and authorizes the write.
#Clear cached data
1import { clearActivityQueryCache } from "ludicord/activity";23clearActivityQueryCache(["profile", userId]);
Omit the key to clear every query and abort active work—for example after logout. The cache bounds itself and prefers removing inactive entries when it grows.
#Query or WebSocket?
Use a query for request/response data with a useful stale window. Use a WebSocket when the server must push updates immediately. A common Activity loads an initial snapshot with a query, then applies live event updates through a socket or shared state.
#Next step
Open Configuration to tune the runtime limits and behavior supporting these client and server features.