Guides
Polling
Refetch a query on an interval while it's on screen with meta.refetchInterval and the polling() feature.
A query with meta: { refetchInterval } refetches every so many milliseconds while a component reads it:
export function function buildQuery(input: {
id: string;
}): QueryOptions<readonly ["builds", string], Build, Build, DefaultTypes>
buildQuery(input: {
id: string;
}
input: { id: stringid: string }) {
return queryOptions<readonly ["builds", string], Build, Build>(options: BaseOptions<readonly ["builds", string], DefaultTypes> & {
readonly queryFn: (context: QueryFunctionContext<readonly ["builds", string]>) => Awaitable<Build>;
readonly select?: ((data: Build) => Build) | undefined;
}): QueryOptions<readonly ["builds", string], Build, Build, DefaultTypes> (+1 overload)
Typed options for a query factory:
queryOptions({
BaseOptions<readonly ["builds", string], DefaultTypes>.queryKey: readonly ["builds", string]A plain array, usually built by a group(). Handles keep its literal type.
queryKey: builds<readonly [string]>(rest_0: string): readonly ["builds", string]builds(input: {
id: string;
}
input.id: stringid),
queryFn: (context: QueryFunctionContext<readonly ["builds", string]>) => Awaitable<Build>queryFn: () => function getBuild(id: string): Promise<Build>getBuild(input: {
id: string;
}
input.id: stringid),
BaseOptions<readonly ["builds", string], DefaultTypes>.meta?: NoInfer<Partial<Readonly<Record<string, unknown>>>> | undefinedSettings for the root's links and features, and data for targets: the
fields the root's parts declare, such as retry with the retry()
link.
meta: { refetchInterval: numberrefetchInterval: 2000 },
});
}
The polling() feature reads that field, so the client needs it, on its own or through recommended():
const const queryClient: QueryClient<{
readonly meta: Flat<Present<PollingMeta>>;
}>
queryClient = createQueryClient<[], readonly [Feature<{
readonly meta: PollingMeta;
}>]>(config?: QueryClientConfig & {
readonly features?: readonly [Feature<{
readonly meta: PollingMeta;
}>] | undefined;
readonly links?: [] | undefined;
}): QueryClient<{
readonly meta: Flat<Present<PollingMeta>>;
}>
Create a query client. Behaviors are opt-in: pass links and features,
or spread recommended(). The client's meta type is what they declare.
createQueryClient({ features?: (readonly AnyFeature[] & readonly [Feature<{
readonly meta: PollingMeta;
}>]) | undefined
Behaviors started while a provider has the client mounted.
features: [function polling(): Feature<{
readonly meta: PollingMeta;
}>
Refetch read queries every meta.refetchInterval ms.
polling()] });
// or
const const sameWithMore: QueryClient<{
readonly meta: Flat<Present<PollingMeta> & Present<FocusRefetchMeta> & Present<ReconnectRefetchMeta> & Present<RetryMeta>>;
}>
sameWithMore = createQueryClient<readonly [Link<{
readonly meta: RetryMeta;
}>], readonly [Feature<{
readonly meta: FocusRefetchMeta;
}>, Feature<{
readonly meta: ReconnectRefetchMeta;
}>, Feature<{
readonly meta: PollingMeta;
}>]>(config?: QueryClientConfig & {
readonly features?: readonly [Feature<{
readonly meta: FocusRefetchMeta;
}>, Feature<{
readonly meta: ReconnectRefetchMeta;
}>, Feature<...>] | undefined;
readonly links?: readonly [...] | undefined;
}): QueryClient<...>
Create a query client. Behaviors are opt-in: pass links and features,
or spread recommended(). The client's meta type is what they declare.
createQueryClient({ ...function recommended(): RecommendedThe behaviors most apps want: retries, refetching on focus and reconnect,
and polling. Spread it into the config:
recommended() });
On a client without polling(), refetchInterval does nothing. A factory built with a bound queryOptions gets the field typed only when the root has the feature: see Query meta.
- Polling starts when the first component reads the query, and stops when the last one unmounts.
- There is one timer per query, however many components read it.
- A poll that lands while another request for the query is in flight joins it instead of starting a second one.
- Features run while a
QueryClientProviderhas the client mounted, so the server never polls.
Each poll is a background refetch: the data on screen stays put. Structural sharing keeps a response equal to the current data from changing the data's identity, so memoized children don't re-render.
The interval per input
The interval is part of the query's options, so a factory can compute it from its input:
export function function buildLogQuery(input: {
id: string;
live: boolean;
}): QueryOptions<readonly ["builds", "log", {
id: string;
live: boolean;
}], Build, Build, DefaultTypes>
buildLogQuery(input: {
id: string;
live: boolean;
}
input: { id: stringid: string; live: booleanlive: boolean }) {
return queryOptions<readonly ["builds", "log", {
id: string;
live: boolean;
}], Build, Build>(options: BaseOptions<readonly ["builds", "log", {
id: string;
live: boolean;
}], DefaultTypes> & {
readonly queryFn: (context: QueryFunctionContext<readonly ["builds", "log", {
id: string;
live: boolean;
}]>) => Awaitable<Build>;
readonly select?: ((data: Build) => Build) | undefined;
}): QueryOptions<readonly ["builds", "log", {
id: string;
live: boolean;
}], Build, Build, DefaultTypes> (+1 overload)
Typed options for a query factory:
queryOptions({
BaseOptions<readonly ["builds", "log", { id: string; live: boolean; }], DefaultTypes>.queryKey: readonly ["builds", "log", {
id: string;
live: boolean;
}]
A plain array, usually built by a group(). Handles keep its literal type.
queryKey: builds<readonly ["log", {
id: string;
live: boolean;
}]>(rest_0: "log", rest_1: {
id: string;
live: boolean;
}): readonly ["builds", "log", {
id: string;
live: boolean;
}]
builds("log", input: {
id: string;
live: boolean;
}
input),
queryFn: (context: QueryFunctionContext<readonly ["builds", "log", {
id: string;
live: boolean;
}]>) => Awaitable<Build>
queryFn: () => function getBuild(id: string): Promise<Build>getBuild(input: {
id: string;
live: boolean;
}
input.id: stringid),
BaseOptions<readonly ["builds", "log", { id: string; live: boolean; }], DefaultTypes>.meta?: NoInfer<Partial<Readonly<Record<string, unknown>>>> | undefinedSettings for the root's links and features, and data for targets: the
fields the root's parts declare, such as retry with the retry()
link.
meta: { refetchInterval: number | booleanrefetchInterval: input: {
id: string;
live: boolean;
}
input.live: booleanlive ? 1000 : false },
});
}false or 0 turns polling off. Each input is its own query, so this picks the interval per query; it can't change while the same query is read.
Polling and staleTime
Polling refetches whatever the data's staleTime: the interval is an explicit request for fresh data. Keep staleTime meaningful anyway, because the loaders that start the same query rely on it.