Skip to content

raf-query is early. The API can still change between minor releases, so use it at your own risk.

raf-query

Guides

Testing

Test components and loaders that use raf-query with a fresh client per test, seeded data, an injectable clock and real browser events.

Components that read queries need a query client and handles. In tests, create both directly. There's nothing to mock in raf-query itself: mock your API, or seed the cache. The examples use Vitest and Testing Library, but nothing depends on them.

A client per test

Create a new client for every test, so no cached data leaks between them:

let let queryClient: QueryClient<any>queryClient: interface QueryClient<TTypes extends RootTypes = any>

A query cache. TTypes is its root's types, which factories built with bindQueryOptions<typeof makeQueryClient>() are checked against. The bare QueryClient type takes a client of any root.

QueryClient
;
function beforeEach(run: () => Promise<void> | void): voidbeforeEach(() => { let queryClient: QueryClient<any>queryClient =
createQueryClient<[], []>(config?: QueryClientConfig & {
    readonly features?: [] | undefined;
    readonly links?: [] | undefined;
}): QueryClient<{
    readonly meta: Flat<unknown>;
}>

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
({ QueryClientConfig.isServer?: boolean | undefined

Default: no window. Server clients never gc and never revalidate cached data.

isServer
: false });
}); function afterEach(run: () => Promise<void> | void): voidafterEach(() => { function removeAll(client: QueryClient): void

Remove every query, aborting their requests.

removeAll
(let queryClient: QueryClient<any>queryClient);
}); function function renderWithClient(ui: ReactNode): Promise<RenderResult<typeof import(".pnpm/@testing-library+dom@10.4.2/node_modules/@testing-library/dom/types/queries"), HTMLElement, HTMLElement>>renderWithClient(ui: ReactNodeui: type ReactNode = string | number | bigint | boolean | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<ReactNode> | React.ReactPortal | Promise<AwaitedReactNode> | null | undefined

Represents all of the things React can render.

ReactNode
) {
return act<RenderResult<typeof import(".pnpm/@testing-library+dom@10.4.2/node_modules/@testing-library/dom/types/queries"), HTMLElement, HTMLElement>>(callback: () => RenderResult<typeof import(".pnpm/@testing-library+dom@10.4.2/node_modules/@testing-library/dom/types/queries"), HTMLElement, HTMLElement> | Promise<RenderResult<typeof import(".pnpm/@testing-library+dom@10.4.2/node_modules/@testing-library/dom/types/queries"), HTMLElement, HTMLElement>>): Promise<...> (+1 overload)

Wrap any code rendering and triggering updates to your components into act() calls.

@paramcallback — A synchronous, void callback that will execute as a single, complete React commit.@seehttps://reactjs.org/blog/2019/02/06/react-v16.8.0.html#testing-hooks
act
(async () =>
function render(ui: React.ReactNode, options?: Omit<RenderOptions<typeof import(".pnpm/@testing-library+dom@10.4.2/node_modules/@testing-library/dom/types/queries"), HTMLElement, HTMLElement>, "queries"> | undefined): RenderResult (+1 overload)

Render into a container which is appended to document.body. It should be used with cleanup.

render
(
<
function QueryClientProvider(props: {
    readonly children?: ReactNode;
    readonly client: QueryClient;
}): React.JSX.Element

Provides a client to the tree, and runs its features (focus and reconnect refetching, polling, …) while mounted.

QueryClientProvider
client: QueryClient<any>client={let queryClient: QueryClient<any>queryClient}>
<const Suspense: React.ExoticComponent<React.SuspenseProps>

Lets you display a fallback until its children have finished loading.

Suspense
React.SuspenseProps.fallback?: ReactNode

A fallback react tree to show when a Suspense child (like React.lazy) suspends

fallback
="Loading…">{ui: ReactNodeui}</const Suspense: React.ExoticComponent<React.SuspenseProps>

Lets you display a fallback until its children have finished loading.

Suspense
>
</
function QueryClientProvider(props: {
    readonly children?: ReactNode;
    readonly client: QueryClient;
}): React.JSX.Element

Provides a client to the tree, and runs its features (focus and reconnect refetching, polling, …) while mounted.

QueryClientProvider
>,
), ); }
  • A client starts with no links and no features: no retries, no refetching on focus, no polling. A failing request fails at once. Add only the parts a test is about. If your app makes its client in a function, let that function take the parts, so tests can leave retry() out.
  • removeAll drops every query and aborts requests in flight, so nothing runs after the test ends. Features stop when the QueryClientProvider unmounts, which Testing Library's cleanup does after each test.
  • isServer: false makes it a browser client even where window isn't defined. See Server rendering.

Render inside an awaited, async act. A tree that suspends inside a sync act, which render on its own uses, is never retried by React once its data lands, with or without a Suspense boundary. The same holds for any use(promise), not only raf-query's.

Testing a component

A component takes a handle as a prop, so a test can start the query itself with load, the same way a loader would:

function test(name: string, run: () => Promise<void> | void): voidtest("shows the post", async () => {
	const [
const handles: {
    readonly post: QueryHandle<(input: {
        slug: string;
    }) => QueryOptions<readonly ["posts", "bySlug", string], Post, Post, DefaultTypes>>;
}
handles
] = await
load<{
    readonly post: LoadEntry<(input: {
        slug: string;
    }) => QueryOptions<readonly ["posts", "bySlug", string], Post, Post, DefaultTypes>, "prefetch">;
}>(client: QueryClient, entries: {
    readonly post: LoadEntry<(input: {
        slug: string;
    }) => QueryOptions<readonly ["posts", "bySlug", string], Post, Post, DefaultTypes>, "prefetch">;
}): Promise<Loaded<{
    readonly post: LoadEntry<(input: {
        slug: string;
    }) => QueryOptions<readonly ["posts", "bySlug", string], Post, Post, DefaultTypes>, "prefetch">;
}>>

Start a loader's queries and return a serializable handle for each. It starts every entry before it waits for any, so the order of the entries can't create a waterfall. Resolves with [handles, data] once every swr and revalidate entry has data, and rejects with the error as soon as one fails. prefetch entries never make it reject. A skipToken in place of an entry starts nothing and gets no handle.

load
(let queryClient: QueryClient<any>queryClient, {
post: LoadEntry<(input: {
    slug: string;
}) => QueryOptions<readonly ["posts", "bySlug", string], Post, Post, DefaultTypes>, "prefetch">
post
:
prefetch<(input: {
    slug: string;
}) => QueryOptions<readonly ["posts", "bySlug", string], Post, Post, DefaultTypes>>(factory: (input: {
    slug: string;
}) => QueryOptions<readonly ["posts", "bySlug", string], Post, Post, DefaultTypes>, ...args: LoadEntryArgs<(input: {
    slug: string;
}) => QueryOptions<readonly ["posts", "bySlug", string], Post, Post, DefaultTypes>>): LoadEntry<(input: {
    slug: string;
}) => QueryOptions<readonly ["posts", "bySlug", string], Post, Post, DefaultTypes>, "prefetch">

Start the query if the cache has no fresh data, and don't wait for it. It never makes the load reject: errors show up where a component reads the handle.

prefetch
(
function postQuery(input: {
    slug: string;
}): QueryOptions<readonly ["posts", "bySlug", string], Post, Post, DefaultTypes>
postQuery
, { slug: stringslug: "hello" }),
}); await function renderWithClient(ui: ReactNode): Promise<RenderResult<typeof import(".pnpm/@testing-library+dom@10.4.2/node_modules/@testing-library/dom/types/queries"), HTMLElement, HTMLElement>>renderWithClient(<
function Post(props: {
    handle: QueryHandle<typeof postQuery>;
}): ReactNode
Post
handle: QueryHandle<(input: {
    slug: string;
}) => QueryOptions<readonly ["posts", "bySlug", string], Post, Post, DefaultTypes>>
handle
={
const handles: {
    readonly post: QueryHandle<(input: {
        slug: string;
    }) => QueryOptions<readonly ["posts", "bySlug", string], Post, Post, DefaultTypes>>;
}
handles
.
post: QueryHandle<(input: {
    slug: string;
}) => QueryOptions<readonly ["posts", "bySlug", string], Post, Post, DefaultTypes>>
post
} />);
function expect(actual: unknown): {
    toBe(expected: unknown): void;
    toBeDefined(): void;
    toBeUndefined(): void;
}
expect
((await const screen: Screen<typeof import(".pnpm/@testing-library+dom@10.4.2/node_modules/@testing-library/dom/types/queries")>screen.findByRole<HTMLElement>(role: ByRoleMatcher, options?: ByRoleOptions | undefined, waitForElementOptions?: waitForOptions | undefined): Promise<HTMLElement> (+1 overload)findByRole("heading")).Element.textContent: stringtextContent).function toBe(expected: unknown): voidtoBe("Hello");
});

Seeding the cache

To render with known data and no request at all, write it with setData before loading:

const 
const queryClient: QueryClient<{
    readonly meta: Flat<unknown>;
}>
queryClient
=
createQueryClient<[], []>(config?: QueryClientConfig & {
    readonly features?: [] | undefined;
    readonly links?: [] | undefined;
}): QueryClient<{
    readonly meta: Flat<unknown>;
}>

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
({
QueryClientConfig.isServer?: boolean | undefined

Default: no window. Server clients never gc and never revalidate cached data.

isServer
: false,
QueryClientConfig.staleTime?: number | undefined

Default staleTime: 0.

staleTime
: var Number: NumberConstructor

An object that represents a number of any kind. All JavaScript numbers are 64-bit floating-point numbers.

Number
.NumberConstructor.POSITIVE_INFINITY: number

A value greater than the largest number that can be represented in JavaScript. JavaScript displays POSITIVE_INFINITY values as infinity.

POSITIVE_INFINITY
,
});
setData<(input: {
    slug: string;
}) => QueryOptions<readonly ["posts", "bySlug", string], Post, Post, DefaultTypes>>(client: ClientFor<(input: {
    slug: string;
}) => QueryOptions<readonly ["posts", "bySlug", string], Post, Post, DefaultTypes>>, factory: (input: {
    slug: string;
}) => QueryOptions<readonly ["posts", "bySlug", string], Post, Post, DefaultTypes>, input: {
    slug: string;
}, updater: Updater<(input: {
    slug: string;
}) => QueryOptions<readonly ["posts", "bySlug", string], Post, Post, DefaultTypes>>): void (+1 overload)

Write a query's data, as if a request had just returned it. updater gets the current data, or undefined if there is none. A factory that takes no input (or an optional one) skips the input: setData(client, todosQuery, updater).

setData
(
const queryClient: QueryClient<{
    readonly meta: Flat<unknown>;
}>
queryClient
,
function postQuery(input: {
    slug: string;
}): QueryOptions<readonly ["posts", "bySlug", string], Post, Post, DefaultTypes>
postQuery
,
{ slug: stringslug: "hello" }, { Post.slug: stringslug: "hello", Post.title: stringtitle: "Hello", Post.body: stringbody: "", Post.authorId: stringauthorId: "" }, ); const [
const handles: {
    readonly post: QueryHandle<(input: {
        slug: string;
    }) => QueryOptions<readonly ["posts", "bySlug", string], Post, Post, DefaultTypes>>;
}
handles
] = await
load<{
    readonly post: LoadEntry<(input: {
        slug: string;
    }) => QueryOptions<readonly ["posts", "bySlug", string], Post, Post, DefaultTypes>, "swr">;
}>(client: QueryClient, entries: {
    readonly post: LoadEntry<(input: {
        slug: string;
    }) => QueryOptions<readonly ["posts", "bySlug", string], Post, Post, DefaultTypes>, "swr">;
}): Promise<Loaded<{
    readonly post: LoadEntry<(input: {
        slug: string;
    }) => QueryOptions<readonly ["posts", "bySlug", string], Post, Post, DefaultTypes>, "swr">;
}>>

Start a loader's queries and return a serializable handle for each. It starts every entry before it waits for any, so the order of the entries can't create a waterfall. Resolves with [handles, data] once every swr and revalidate entry has data, and rejects with the error as soon as one fails. prefetch entries never make it reject. A skipToken in place of an entry starts nothing and gets no handle.

load
(
const queryClient: QueryClient<{
    readonly meta: Flat<unknown>;
}>
queryClient
, {
post: LoadEntry<(input: {
    slug: string;
}) => QueryOptions<readonly ["posts", "bySlug", string], Post, Post, DefaultTypes>, "swr">
post
:
swr<(input: {
    slug: string;
}) => QueryOptions<readonly ["posts", "bySlug", string], Post, Post, DefaultTypes>>(factory: (input: {
    slug: string;
}) => QueryOptions<readonly ["posts", "bySlug", string], Post, Post, DefaultTypes>, ...args: LoadEntryArgs<(input: {
    slug: string;
}) => QueryOptions<readonly ["posts", "bySlug", string], Post, Post, DefaultTypes>>): LoadEntry<(input: {
    slug: string;
}) => QueryOptions<readonly ["posts", "bySlug", string], Post, Post, DefaultTypes>, "swr">

Use cached data even when it's stale, and wait only when the cache has none. Stale data resolves at once and refetches in the background, as HTTP's stale-while-revalidate does. The load rejects if the fetch it waits for fails.

swr
(
function postQuery(input: {
    slug: string;
}): QueryOptions<readonly ["posts", "bySlug", string], Post, Post, DefaultTypes>
postQuery
, { slug: stringslug: "hello" }),
});

swr resolves at once when the cache has data. It also refetches stale data in the background, and data written with setData is fresh only for staleTime, which is 0 by default: give the test client a staleTime so the seeded data stays as it is.

Mocking the network

The queryFn is your code, so mock what it calls. With fetch, MSW intercepts requests without changing application code:

const server: {
    use(...handlers: ReadonlyArray<unknown>): void;
}
server
.function use(...handlers: ReadonlyArray<unknown>): voiduse(
const http: {
    get(path: string, resolver: () => unknown): unknown;
}
http
.function get(path: string, resolver: () => unknown): unknownget("/api/posts/:slug", () =>
const HttpResponse: {
    json(body: unknown): unknown;
}
HttpResponse
.function json(body: unknown): unknownjson({ slug: stringslug: "hello", title: stringtitle: "Hello" }),
), );

Testing loaders

A loader is a function of its context. Export it from the route file, call it with a client, and check what it started:

function test(name: string, run: () => Promise<void> | void): voidtest("the post loader starts both queries", async () => {
	const 
const queryClient: QueryClient<{
    readonly meta: Flat<unknown>;
}>
queryClient
=
createQueryClient<[], []>(config?: QueryClientConfig & {
    readonly features?: [] | undefined;
    readonly links?: [] | undefined;
}): QueryClient<{
    readonly meta: Flat<unknown>;
}>

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
({ QueryClientConfig.isServer?: boolean | undefined

Default: no window. Server clients never gc and never revalidate cached data.

isServer
: false });
await
function postLoader(args: {
    context: {
        queryClient: QueryClient;
    };
    params: {
        slug: string;
    };
}): Promise<{
    readonly comments: QueryHandle<(input: {
        slug: string;
    }) => InfiniteQueryOptions<readonly ["posts", "comments", string], CommentPage, number, InfiniteData<CommentPage, number>, DefaultTypes>>;
    readonly post: QueryHandle<(input: {
        slug: string;
    }) => QueryOptions<readonly ["posts", "bySlug", string], Post, Post, DefaultTypes>>;
}>
postLoader
({
context: {
    queryClient: QueryClient;
}
context
: { queryClient: QueryClient<any>queryClient },
params: {
    slug: string;
}
params
: { slug: stringslug: "hello" },
});
function expect(actual: unknown): {
    toBe(expected: unknown): void;
    toBeDefined(): void;
    toBeUndefined(): void;
}
expect
(
getData<(input: {
    slug: string;
}) => QueryOptions<readonly ["posts", "bySlug", string], Post, Post, DefaultTypes>>(client: ClientFor<(input: {
    slug: string;
}) => QueryOptions<readonly ["posts", "bySlug", string], Post, Post, DefaultTypes>>, factory: (input: {
    slug: string;
}) => QueryOptions<readonly ["posts", "bySlug", string], Post, Post, DefaultTypes>, input: {
    slug: string;
}): Post | undefined

A query's cached data (before select), or undefined.

getData
(
const queryClient: QueryClient<{
    readonly meta: Flat<unknown>;
}>
queryClient
,
function postQuery(input: {
    slug: string;
}): QueryOptions<readonly ["posts", "bySlug", string], Post, Post, DefaultTypes>
postQuery
, { slug: stringslug: "hello" })).function toBeDefined(): voidtoBeDefined();
function expect(actual: unknown): {
    toBe(expected: unknown): void;
    toBeDefined(): void;
    toBeUndefined(): void;
}
expect
(function isFetching(client: QueryClient, target?: AnyInvalidation): number

How many queries have a request in flight, optionally only a factory's, a group's or a target's.

isFetching
(
const queryClient: QueryClient<{
    readonly meta: Flat<unknown>;
}>
queryClient
,
function commentsQuery(input: {
    slug: string;
}): InfiniteQueryOptions<readonly ["posts", "comments", string], CommentPage, number, InfiniteData<CommentPage, number>, DefaultTypes>
commentsQuery
)).function toBe(expected: unknown): voidtoBe(1);
});

getData reads what an swr or revalidate entry loaded, and isFetching(queryClient, target) counts the requests still in flight, such as a prefetch's.

Time

Staleness, garbage collection, retry delays, timeouts and polling depend on time. The client reads it from its clock: now(), setTimeout and clearTimeout. Pass your own to createQueryClient({ clock }), and a test of gcTime or staleTime runs in milliseconds, without touching globals:


function test(name: string, run: () => Promise<void> | void): voidtest("garbage-collects unread entries", () => {
	const 
const clock: Clock & {
    advance(ms: number): void;
}
clock
=
function fakeClock(): Clock & {
    advance(ms: number): void;
}

A clock whose timers fire only when the test advances it.

fakeClock
();
const
const queryClient: QueryClient<{
    readonly meta: Flat<unknown>;
}>
queryClient
=
createQueryClient<[], []>(config?: QueryClientConfig & {
    readonly features?: [] | undefined;
    readonly links?: [] | undefined;
}): QueryClient<{
    readonly meta: Flat<unknown>;
}>

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
({ QueryClientConfig.clock?: Clock | undefinedclock, QueryClientConfig.isServer?: boolean | undefined

Default: no window. Server clients never gc and never revalidate cached data.

isServer
: false });
const const post: Postpost: Post = { Post.slug: stringslug: "hello", Post.title: stringtitle: "Hello", Post.body: stringbody: "", Post.authorId: stringauthorId: "ada", };
setData<(input: {
    slug: string;
}) => QueryOptions<readonly ["posts", "bySlug", string], Post, Post, DefaultTypes>>(client: ClientFor<(input: {
    slug: string;
}) => QueryOptions<readonly ["posts", "bySlug", string], Post, Post, DefaultTypes>>, factory: (input: {
    slug: string;
}) => QueryOptions<readonly ["posts", "bySlug", string], Post, Post, DefaultTypes>, input: {
    slug: string;
}, updater: Updater<(input: {
    slug: string;
}) => QueryOptions<readonly ["posts", "bySlug", string], Post, Post, DefaultTypes>>): void (+1 overload)

Write a query's data, as if a request had just returned it. updater gets the current data, or undefined if there is none. A factory that takes no input (or an optional one) skips the input: setData(client, todosQuery, updater).

setData
(
const queryClient: QueryClient<{
    readonly meta: Flat<unknown>;
}>
queryClient
,
function postQuery(input: {
    slug: string;
}): QueryOptions<readonly ["posts", "bySlug", string], Post, Post, DefaultTypes>
postQuery
, { slug: stringslug: "hello" }, const post: Postpost);
const clock: Clock & {
    advance(ms: number): void;
}
clock
.function advance(ms: number): voidadvance(5 * 60 * 1000); // the default gcTime
function expect(actual: unknown): {
    toBe(expected: unknown): void;
    toBeDefined(): void;
    toBeUndefined(): void;
}
expect
(
getData<(input: {
    slug: string;
}) => QueryOptions<readonly ["posts", "bySlug", string], Post, Post, DefaultTypes>>(client: ClientFor<(input: {
    slug: string;
}) => QueryOptions<readonly ["posts", "bySlug", string], Post, Post, DefaultTypes>>, factory: (input: {
    slug: string;
}) => QueryOptions<readonly ["posts", "bySlug", string], Post, Post, DefaultTypes>, input: {
    slug: string;
}): Post | undefined

A query's cached data (before select), or undefined.

getData
(
const queryClient: QueryClient<{
    readonly meta: Flat<unknown>;
}>
queryClient
,
function postQuery(input: {
    slug: string;
}): QueryOptions<readonly ["posts", "bySlug", string], Post, Post, DefaultTypes>
postQuery
, { slug: stringslug: "hello" })).function toBeUndefined(): voidtoBeUndefined();
});

The default clock calls Date.now() and setTimeout each time, so vi.useFakeTimers() works as well.

Focus and the network

focusRefetch() and reconnectRefetch() listen to the page's own events, so a test in a DOM environment fires them. Give the client the feature, render under a QueryClientProvider (features only run while one has the client mounted), and dispatch the event:

function test(name: string, run: () => Promise<void> | void): voidtest("refetches on focus", async () => {
	let let calls: numbercalls = 0;
	const const countQuery: () => QueryOptions<readonly ["count"], number, number, DefaultTypes>countQuery = () =>
		
queryOptions<readonly ["count"], number, number>(options: BaseOptions<readonly ["count"], DefaultTypes> & {
    readonly queryFn: (context: QueryFunctionContext<readonly ["count"]>) => Awaitable<number>;
    readonly select?: ((data: number) => number) | undefined;
}): QueryOptions<readonly ["count"], number, number, DefaultTypes> (+1 overload)

Typed options for a query factory:

queryOptions
({ BaseOptions<readonly ["count"], DefaultTypes>.queryKey: readonly ["count"]

A plain array, usually built by a group(). Handles keep its literal type.

queryKey
:
const count: Group
<readonly []>() => readonly ["count"]
count
(), queryFn: (context: QueryFunctionContext<readonly ["count"]>) => Awaitable<number>queryFn: async () => ++let calls: numbercalls });
function
function (local function) Count(props: {
    handle: QueryHandle<() => QueryOptions<readonly ["count"], number, number, DefaultTypes>>;
}): React.JSX.Element
Count
(
props: {
    handle: QueryHandle<() => QueryOptions<readonly ["count"], number, number, DefaultTypes>>;
}
props
: { handle: QueryHandle<() => QueryOptions<readonly ["count"], number, number, DefaultTypes>>handle: interface QueryHandle<TFactory extends QueryFactory = (input: unknown) => { readonly queryFn: (context: never) => unknown; readonly queryKey: QueryKey; }>

A small serializable reference to a query: its key and its factory's input. It carries no data; readers get that from the cache. Handles of different factories have different types.

QueryHandle
<typeof const countQuery: () => QueryOptions<readonly ["count"], number, number, DefaultTypes>countQuery> }) {
const [const countThenable: QueryValue<() => QueryOptions<readonly ["count"], number, number, DefaultTypes>>countThenable] = useQuery<() => QueryOptions<readonly ["count"], number, number, DefaultTypes>>(factory: () => QueryOptions<readonly ["count"], number, number, DefaultTypes>, handle: QueryHandle<() => QueryOptions<readonly ["count"], number, number, DefaultTypes>>, options?: UseQueryOptions): readonly [QueryValue<() => QueryOptions<readonly ["count"], number, number, DefaultTypes>>, QueryState<() => QueryOptions<readonly ["count"], number, number, DefaultTypes>>]

Read a query by its handle.

useQuery
(const countQuery: () => QueryOptions<readonly ["count"], number, number, DefaultTypes>countQuery,
props: {
    handle: QueryHandle<() => QueryOptions<readonly ["count"], number, number, DefaultTypes>>;
}
props
.handle: QueryHandle<() => QueryOptions<readonly ["count"], number, number, DefaultTypes>>handle);
const const count: numbercount = use<number>(usable: React.Usable<number>): numberuse(const countThenable: QueryValue<() => QueryOptions<readonly ["count"], number, number, DefaultTypes>>countThenable); return <React.JSX.IntrinsicElements.p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>p>{const count: numbercount}</React.JSX.IntrinsicElements.p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>p>; } let queryClient: QueryClient<any>queryClient =
createQueryClient<[], readonly [Feature<{
    readonly meta: FocusRefetchMeta;
}>]>(config?: QueryClientConfig & {
    readonly features?: readonly [Feature<{
        readonly meta: FocusRefetchMeta;
    }>] | undefined;
    readonly links?: [] | undefined;
}): QueryClient<{
    readonly meta: Flat<Present<FocusRefetchMeta>>;
}>

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
({
QueryClientConfig.isServer?: boolean | undefined

Default: no window. Server clients never gc and never revalidate cached data.

isServer
: false,
features?: (readonly AnyFeature[] & readonly [Feature<{
    readonly meta: FocusRefetchMeta;
}>]) | undefined

Behaviors started while a provider has the client mounted.

features
: [
function focusRefetch(): Feature<{
    readonly meta: FocusRefetchMeta;
}>

Refetch read, stale queries when the window regains focus, except those with meta: { refetchOnWindowFocus: false }.

focusRefetch
()],
}); const [
const handles: {
    readonly count: QueryHandle<() => QueryOptions<readonly ["count"], number, number, DefaultTypes>>;
}
handles
] = await
load<{
    readonly count: LoadEntry<() => QueryOptions<readonly ["count"], number, number, DefaultTypes>, "revalidate">;
}>(client: QueryClient, entries: {
    readonly count: LoadEntry<() => QueryOptions<readonly ["count"], number, number, DefaultTypes>, "revalidate">;
}): Promise<Loaded<{
    readonly count: LoadEntry<() => QueryOptions<readonly ["count"], number, number, DefaultTypes>, "revalidate">;
}>>

Start a loader's queries and return a serializable handle for each. It starts every entry before it waits for any, so the order of the entries can't create a waterfall. Resolves with [handles, data] once every swr and revalidate entry has data, and rejects with the error as soon as one fails. prefetch entries never make it reject. A skipToken in place of an entry starts nothing and gets no handle.

load
(let queryClient: QueryClient<any>queryClient, {
count: LoadEntry<() => QueryOptions<readonly ["count"], number, number, DefaultTypes>, "revalidate">count: revalidate<() => QueryOptions<readonly ["count"], number, number, DefaultTypes>>(factory: () => QueryOptions<readonly ["count"], number, number, DefaultTypes>, ...args: LoadEntryArgs<() => QueryOptions<readonly ["count"], number, number, DefaultTypes>>): LoadEntry<() => QueryOptions<readonly ["count"], number, number, DefaultTypes>, "revalidate">

Wait for fresh data, as HTTP's must-revalidate does. Fresh cached data resolves at once without a refetch (staleTime is the max-age); with stale data or none it waits for the fetch. On a server client, data counts as fresh until a write invalidates it. The load rejects if the fetch fails.

revalidate
(const countQuery: () => QueryOptions<readonly ["count"], number, number, DefaultTypes>countQuery),
}); await function renderWithClient(ui: ReactNode): Promise<RenderResult<typeof import(".pnpm/@testing-library+dom@10.4.2/node_modules/@testing-library/dom/types/queries"), HTMLElement, HTMLElement>>renderWithClient(<
function (local function) Count(props: {
    handle: QueryHandle<() => QueryOptions<readonly ["count"], number, number, DefaultTypes>>;
}): React.JSX.Element
Count
handle: QueryHandle<() => QueryOptions<readonly ["count"], number, number, DefaultTypes>>handle={
const handles: {
    readonly count: QueryHandle<() => QueryOptions<readonly ["count"], number, number, DefaultTypes>>;
}
handles
.count: QueryHandle<() => QueryOptions<readonly ["count"], number, number, DefaultTypes>>count} />);
await act<void>(callback: () => void | Promise<void>): Promise<void> (+1 overload)

Wrap any code rendering and triggering updates to your components into act() calls.

@paramcallback — A synchronous, void callback that will execute as a single, complete React commit.@seehttps://reactjs.org/blog/2019/02/06/react-v16.8.0.html#testing-hooks
act
(async () => {
var window: Window & typeof globalThis

The window property of a Window object points to the window object itself.

window
.function dispatchEvent(event: Event): boolean

The dispatchEvent() method of the EventTarget sends an Event to the object, (synchronously) invoking the affected event listeners in the appropriate order. The normal event processing rules (including the capturing and optional bubbling phase) also apply to events dispatched manually with dispatchEvent().

dispatchEvent
(new var Event: new (type: string, eventInitDict?: EventInit) => Event

The Event interface represents an event which takes place on an EventTarget.

Event
("focus"));
});
function expect(actual: unknown): {
    toBe(expected: unknown): void;
    toBeDefined(): void;
    toBeUndefined(): void;
}
expect
(let calls: numbercalls).function toBe(expected: unknown): voidtoBe(2);
});

focusRefetch() refetches on focus only while document.visibilityState is "visible". For reconnecting, dispatch new Event("online"). Retries wait for that event too, while navigator.onLine is false.

Server rendering

A client created where window isn't defined is a server client: no retries, no garbage collection, and cached data is never revalidated. To test browser behavior in Node without a DOM environment, pass isServer: false:

const 
const queryClient: QueryClient<{
    readonly meta: Flat<unknown>;
}>
queryClient
=
createQueryClient<[], []>(config?: QueryClientConfig & {
    readonly features?: [] | undefined;
    readonly links?: [] | undefined;
}): QueryClient<{
    readonly meta: Flat<unknown>;
}>

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
({ QueryClientConfig.isServer?: boolean | undefined

Default: no window. Server clients never gc and never revalidate cached data.

isServer
: false });