Guides
Query cancellation
Pass the queryFn's AbortSignal to fetch so requests nobody needs any more are aborted, and know when raf-query aborts them.
Every queryFn receives an AbortSignal as signal. Pass it to fetch (or any API that takes a signal), and raf-query can abort the request when its result is no longer needed:
export const const search: Group<readonly ["search"]>search = group<"search">(name: "search"): Group<readonly ["search"]> (+1 overload)A group, or a subgroup inside parent. Call it for a query's key: a
flat, plain array that starts with the group's path. Invalidating a group
covers its subgroups and every query below them.
group("search");
export function function searchQuery(input: {
q: string;
}): QueryOptions<readonly ["search", string], readonly SearchResult[], readonly SearchResult[], DefaultTypes>
searchQuery(input: {
q: string;
}
input: { q: stringq: string }) {
return queryOptions<readonly ["search", string], readonly SearchResult[], readonly SearchResult[]>(options: BaseOptions<readonly ["search", string], DefaultTypes> & {
readonly queryFn: (context: QueryFunctionContext<readonly ["search", string]>) => Awaitable<readonly SearchResult[]>;
readonly select?: ((data: readonly SearchResult[]) => readonly SearchResult[]) | undefined;
}): QueryOptions<readonly ["search", string], readonly SearchResult[], readonly SearchResult[], DefaultTypes> (+1 overload)
Typed options for a query factory:
queryOptions({
BaseOptions<readonly ["search", string], DefaultTypes>.queryKey: readonly ["search", string]A plain array, usually built by a group(). Handles keep its literal type.
queryKey: const search: Group
<readonly [string]>(rest_0: string) => readonly ["search", string]
search(input: {
q: string;
}
input.q: stringq),
queryFn: (context: QueryFunctionContext<readonly ["search", string]>) => Awaitable<readonly SearchResult[]>queryFn: async (context: QueryFunctionContext<readonly ["search", string]>context) => {
const const response: Responseresponse = await function fetch(input: string | URL | Request, init?: RequestInit): Promise<Response> (+1 overload)fetch(
`/api/search?q=${function encodeURIComponent(uriComponent: string | number | boolean): stringEncodes a text string as a valid component of a Uniform Resource Identifier (URI).
encodeURIComponent(input: {
q: string;
}
input.q: stringq)}`,
{ RequestInit.signal?: AbortSignal | null | undefinedAn AbortSignal to set request's signal.
signal: context: QueryFunctionContext<readonly ["search", string]>context.QueryFunctionContext<readonly ["search", string]>.signal: AbortSignalAborted when the request is restarted or its entry is removed.
signal },
);
return (await const response: Responseresponse.Body.json(): Promise<any>json()) as interface ReadonlyArray<T>ReadonlyArray<SearchResult>;
},
});
}
A queryFn that ignores the signal still works. Its request just runs to the end, and if raf-query no longer wants it, the result is dropped.
When requests are aborted
raf-query aborts a request only when it replaces or drops it:
- A query on screen is invalidated, or any query is reset, while a request is in flight. That request may have read data from before the write, so it's aborted and a new one starts. Whoever was waiting for the old request gets the new one's result.
- The query is removed (
removeOne,removeMany,removeAll). Its request is aborted, and aloadwaiting for it rejects with anAbortError, as an abortedfetchwould. - A
timeout(ms)link gives up on it. See Retries and errors.
Nothing else aborts a request. When the last component reading a query unmounts, its request runs to the end and the result is cached: the query is likely to be read again, and gcTime only removes an entry once its request has finished. Requests nothing has read yet, like a loader's prefetch, run to the end too.
Try it below. The inbox's queryFn passes its signal to the server, so aborted requests show up in the Queries panel:
Cancelling yourself
To stop a request yourself, remove the query with removeOne(queryClient, factory, input): the request is aborted and the data dropped. There's no call that aborts a request and keeps the query; in practice, letting a request finish and caching its result is almost always what you want.
Using axios or other clients
Clients that accept a signal work the same way, such as axios:
queryOptions<readonly ["posts", "bySlug", string], unknown, unknown>(options: BaseOptions<readonly ["posts", "bySlug", string], DefaultTypes> & {
readonly queryFn: (context: QueryFunctionContext<readonly ["posts", "bySlug", string]>) => unknown;
readonly select?: ((data: unknown) => unknown) | undefined;
}): QueryOptions<readonly ["posts", "bySlug", string], unknown, unknown, DefaultTypes> (+1 overload)
Typed options for a query factory:
queryOptions({
BaseOptions<readonly ["posts", "bySlug", string], DefaultTypes>.queryKey: readonly ["posts", "bySlug", string]A plain array, usually built by a group(). Handles keep its literal type.
queryKey: posts<readonly ["bySlug", string]>(rest_0: "bySlug", rest_1: string): readonly ["posts", "bySlug", string]posts("bySlug", const input: {
slug: string;
}
input.slug: stringslug),
queryFn: (context: QueryFunctionContext<readonly ["posts", "bySlug", string]>) => unknownqueryFn: (context: QueryFunctionContext<readonly ["posts", "bySlug", string]>context) =>
const axios: {
get(url: string, options: {
signal: AbortSignal;
}): Promise<unknown>;
}
axios.function get(url: string, options: {
signal: AbortSignal;
}): Promise<unknown>
get(`/api/posts/${const input: {
slug: string;
}
input.slug: stringslug}`, { signal: AbortSignalsignal: context: QueryFunctionContext<readonly ["posts", "bySlug", string]>context.QueryFunctionContext<readonly ["posts", "bySlug", string]>.signal: AbortSignalAborted when the request is restarted or its entry is removed.
signal }),
});
For clients that take a cancel function instead, call it from an abort listener:
queryOptions<readonly ["posts", "bySlug", string], unknown, unknown>(options: BaseOptions<readonly ["posts", "bySlug", string], DefaultTypes> & {
readonly queryFn: (context: QueryFunctionContext<readonly ["posts", "bySlug", string]>) => unknown;
readonly select?: ((data: unknown) => unknown) | undefined;
}): QueryOptions<readonly ["posts", "bySlug", string], unknown, unknown, DefaultTypes> (+1 overload)
Typed options for a query factory:
queryOptions({
BaseOptions<readonly ["posts", "bySlug", string], DefaultTypes>.queryKey: readonly ["posts", "bySlug", string]A plain array, usually built by a group(). Handles keep its literal type.
queryKey: posts<readonly ["bySlug", string]>(rest_0: "bySlug", rest_1: string): readonly ["posts", "bySlug", string]posts("bySlug", const input: {
slug: string;
}
input.slug: stringslug),
queryFn: (context: QueryFunctionContext<readonly ["posts", "bySlug", string]>) => unknownqueryFn: (context: QueryFunctionContext<readonly ["posts", "bySlug", string]>context) => {
const const request: {
cancel(): void;
promise: Promise<unknown>;
}
request = const client: {
get(url: string): {
cancel(): void;
promise: Promise<unknown>;
};
}
client.function get(url: string): {
cancel(): void;
promise: Promise<unknown>;
}
get(`/api/posts/${const input: {
slug: string;
}
input.slug: stringslug}`);
context: QueryFunctionContext<readonly ["posts", "bySlug", string]>context.QueryFunctionContext<TKey extends QueryKey = QueryKey>.signal: AbortSignalAborted when the request is restarted or its entry is removed.
signal.AbortSignal.addEventListener<"abort">(type: "abort", listener: (this: AbortSignal, ev: Event) => any, options?: boolean | AddEventListenerOptions): void (+1 overload)The addEventListener() method of the EventTarget interface sets up a function that will be called whenever the specified event is delivered to the target.
addEventListener("abort", () => const request: {
cancel(): void;
promise: Promise<unknown>;
}
request.function cancel(): voidcancel());
return const request: {
cancel(): void;
promise: Promise<unknown>;
}
request.promise: Promise<unknown>promise;
},
});