useApiQuery — React hook for GET requests
A React Query–style hook over an ApiClient endpoint for reading data (GET). It is a thin layer on
top of endpoint.request(params).subscribe(...) — deduplication, the tag cache, retry and abort already
live in the endpoint (see ApiClient). The hook adds the React part: subscription,
a stable params key, enabled/refetch, an SSR fast-path (no loading flash) and auto-refetch on cache
invalidation.
The ApiClient is standalone (it depends neither on RxJS/reactive nor on createSynapse), so you can
use just synapse-storage/api + synapse-storage/core + these hooks — without the whole state-manager.
Import
import { useApiQuery } from 'synapse-storage/react'Usage
The hook takes an endpoint (from getEndpoints()), so params/data types are inferred from it.
const endpoints = pokemonApiClient.getEndpoints()
function PokemonCard({ id }: { id: number }) {
// GET: starts on mount, re-runs when params change
const { data, isLoading, isError, error, refetch, fromCache } = useApiQuery(endpoints.getDetails, { id })
if (isLoading) return <Spinner />
if (isError) return <Error message={error?.message} />
return (
<div>
<h3>{data?.name}</h3>
{fromCache && <small>from cache</small>}
<button onClick={refetch}>Refresh</button>
</div>
)
}Return value
useApiQuery(endpoint, params, options?) returns:
| Field | Type | Description |
|---|---|---|
| data | TData | undefined | Response data (or cached data) |
| error | Error | undefined | Request error |
| status | 'idle' | 'loading' | 'success' | 'error' | Current status |
| isLoading | boolean | status === 'loading' |
| isError | boolean | status === 'error' |
| isSuccess | boolean | status === 'success' |
| fromCache | boolean | Data came from cache rather than the network |
| refetch | () => void | Force a re-request |
Options
Extends QueryOptions (signal, headers, timeout, disableCache, retry, …) plus:
enabled(defaulttrue) — whenfalse, the request is not performed (lazy). Useful when params aren't ready yet:refetchOnInvalidate(defaulttrue) — auto-refetch the active query after its cache tags get invalidated by a mutation (see auto-refetch).
SSR: no loading flash after hydration
The lazy initial state reads the cache synchronously via
endpoint.getCachedSync(). On the server
useEffect doesn't run, so the very first (and only) render returns the seeded/cached data; on the client
the first render after hydration shows the server data
immediately instead of flashing loading.
This works only for synchronous storages (MemoryStorage/LocalStorage) and endpoints without
cache-affecting headers. Otherwise the hook falls back to the regular async path.
Auto-refetch on cache invalidation
When a mutation succeeds with invalidatesTags, the matching cache entries are removed and an
invalidation event is emitted. An active useApiQuery whose endpoint tags intersect the invalidated
tags re-runs automatically (parity with React Query — the query "comes alive" instead of waiting for
the TTL). Under the hood this uses
endpoint.onCacheInvalidate(). Turn
it off with refetchOnInvalidate: false.
// getList endpoint: tags: ['PokemonList']
const list = useApiQuery(endpoints.getList, { limit: 12 })
// elsewhere — a mutation with invalidatesTags: ['PokemonList']
// → `list` refetches on its ownNotes
Stable params key. Params are serialized with sorted keys, so a fresh
{ id: 1 }object on every render does not cause an infinite re-request.StrictMode-safe. The effect aborts the in-flight request on cleanup.
paramsidentity doesn't matter. You can pass an inline object literal — only the serialized key drives re-fetching.
See also
ApiClient — the native client, endpoints, caching and SSR.
useApiMutation — the companion hook for writes.