UNPKG

@tanstack/query-core

Version:

The framework agnostic core that powers TanStack Query

1,173 lines (1,095 loc) • 40.6 kB
import { functionalUpdate, hashKey, hashQueryKeyByOptions, noop, partialMatchKey, resolveQueryValue, skipToken, } from './utils' import { QueryCache } from './queryCache' import { MutationCache } from './mutationCache' import { focusManager } from './focusManager' import { onlineManager } from './onlineManager' import { notifyManager } from './notifyManager' import type { CancelOptions, DefaultError, DefaultOptions, DefaultedQueryObserverOptions, EnsureInfiniteQueryDataOptions, EnsureQueryDataOptions, FetchInfiniteQueryOptions, FetchQueryOptions, InferDataFromTag, InferErrorFromTag, InfiniteData, InfiniteQueryExecuteOptions, InvalidateOptions, InvalidateQueryFilters, MutationKey, MutationObserverOptions, MutationOptions, OmitKeyof, QueryClientConfig, QueryExecuteOptions, QueryKey, QueryObserverOptions, QueryOptions, RefetchOptions, RefetchQueryFilters, ResetOptions, SetDataOptions, } from './types' import type { QueryState } from './query' import type { MutationFilters, QueryFilters, Updater } from './utils' // TYPES interface QueryDefaults { queryKey: QueryKey defaultOptions: OmitKeyof<QueryOptions<any, any, any>, 'queryKey'> } interface MutationDefaults { mutationKey: MutationKey defaultOptions: MutationOptions<any, any, any, any> } // CLASS /** * `QueryClient` is used to interact with a cache of queries and mutations. It owns a * `QueryCache` and a `MutationCache` (creating default ones if none are passed in) and holds * the default options that are applied to queries and mutations created through it. * @example * ```ts * const queryClient = new QueryClient({ * defaultOptions: { * queries: { * staleTime: Infinity, * }, * }, * }) * * await queryClient.query({ queryKey: ['posts'], queryFn: fetchPosts }) * ``` */ export class QueryClient { #queryCache: QueryCache #mutationCache: MutationCache #defaultOptions: DefaultOptions #queryDefaults: Map<string, QueryDefaults> #mutationDefaults: Map<string, MutationDefaults> #mountCount: number #unsubscribeFocus?: () => void #unsubscribeOnline?: () => void constructor(config: QueryClientConfig = {}) { this.#queryCache = config.queryCache || new QueryCache() this.#mutationCache = config.mutationCache || new MutationCache() this.#defaultOptions = config.defaultOptions || {} this.#queryDefaults = new Map() this.#mutationDefaults = new Map() this.#mountCount = 0 } /** * Called by a framework adapter's `QueryClientProvider`-equivalent when it mounts, to start * listening for focus/online events and resume paused mutations. Ref-counted via an internal * mount count, so nested or multiple providers sharing the same `QueryClient` don't tear down * the shared listeners until the last one unmounts. */ mount(): void { this.#mountCount++ if (this.#mountCount !== 1) return this.#unsubscribeFocus = focusManager.subscribe(async (focused) => { if (focused) { await this.resumePausedMutations() this.#queryCache.onFocus() } }) this.#unsubscribeOnline = onlineManager.subscribe(async (online) => { if (online) { await this.resumePausedMutations() this.#queryCache.onOnline() } }) } /** * The inverse of {@link QueryClient#mount} — called by a framework adapter's * `QueryClientProvider`-equivalent when it unmounts. Only tears down the focus/online * listeners once the mount count returns to `0`. */ unmount(): void { this.#mountCount-- if (this.#mountCount !== 0) return this.#unsubscribeFocus?.() this.#unsubscribeFocus = undefined this.#unsubscribeOnline?.() this.#unsubscribeOnline = undefined } /** * Returns the number of queries in the cache that are currently fetching, optionally * matching a set of filters. This includes background-fetching, loading new pages, and * loading more infinite query results. * @param filters - Narrows down which fetching queries are counted. Without filters, every * fetching query is counted. * @returns The number of matching queries whose `fetchStatus` is `'fetching'`. * @example * ```ts * if (queryClient.isFetching()) { * console.log('At least one query is fetching!') * } * ``` */ isFetching<TQueryFilters extends QueryFilters<any> = QueryFilters>( filters?: TQueryFilters, ): number { return this.#queryCache.findAll({ ...filters, fetchStatus: 'fetching' }) .length } /** * Returns the number of mutations in the cache that are currently pending, optionally * matching a set of filters. * @param filters - Narrows down which pending mutations are counted. Without filters, every * pending mutation is counted. * @returns The number of matching mutations whose `status` is `'pending'`. * @example * ```ts * if (queryClient.isMutating()) { * console.log('At least one mutation is pending!') * } * ``` */ isMutating< TMutationFilters extends MutationFilters<any, any> = MutationFilters, >(filters?: TMutationFilters): number { return this.#mutationCache.findAll({ ...filters, status: 'pending' }).length } /** * Imperative (non-reactive) way to retrieve data for a QueryKey. * Should only be used in callbacks or functions where reading the latest data is necessary, e.g. for optimistic updates. * * Hint: Do not use this function inside a component, because it won't receive updates. * Use `useQuery` to create a `QueryObserver` that subscribes to changes. * @param queryKey - The query key of the query to read. * @returns The cached data for the query, or `undefined` if no query with this key has been observed yet. * @see {@link QueryClient#getQueriesData} */ getQueryData< TQueryFnData = unknown, TTaggedQueryKey extends QueryKey = QueryKey, TInferredQueryFnData = InferDataFromTag<TQueryFnData, TTaggedQueryKey>, >(queryKey: TTaggedQueryKey): TInferredQueryFnData | undefined { const options = this.defaultQueryOptions({ queryKey }) return this.#queryCache.get<TInferredQueryFnData>(options.queryHash)?.state .data } /** * Returns the cached data of a query, or fetches it if there is none. With `revalidateIfStale`, * stale cached data is also refetched in the background. * @param options - The query options. If the query has no cached data yet, it is fetched with * these options. * @returns A promise that resolves to the cached data, or to the fetched data if nothing was * cached yet. * @deprecated Use queryClient.query({ ...options, staleTime: 'static' }) instead. This method will be removed in the next major version. */ ensureQueryData< TQueryFnData, TError = DefaultError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( options: EnsureQueryDataOptions<TQueryFnData, TError, TData, TQueryKey>, ): Promise<TData> { const defaultedOptions = this.defaultQueryOptions(options) const query = this.#queryCache.build(this, defaultedOptions) const cachedData = query.state.data if (cachedData === undefined) { // oxlint-disable-next-line tanstack-query/no-restricted-syntax return this.fetchQuery(options) } if ( options.revalidateIfStale && query.isStaleByTime(resolveQueryValue(defaultedOptions.staleTime, query)) ) { // oxlint-disable-next-line tanstack-query/no-restricted-syntax void this.prefetchQuery(defaultedOptions) } return Promise.resolve(cachedData) } /** * Imperative (non-reactive) way to retrieve the cached data of multiple queries at once. * Only queries matching the given filters are returned; if none match, an empty array is * returned. * * Because the matched queries can hold data of different shapes (e.g. a broad filter can match * queries with unrelated data types), the `TQueryFnData` generic defaults to `unknown` rather * than being inferred. Passing a more specific type is a convenience for call sites that know * every matched query holds the same shape — it is not checked against the actual cache * contents. * @param filters - The filters that select which queries to read. * @returns An array of query key and data pairs. The data is `undefined` for a query with no cached data. * @see {@link QueryClient#getQueryData} * @example * ```ts * const data = queryClient.getQueriesData({ queryKey: ['posts'] }) * ``` */ getQueriesData< TQueryFnData = unknown, TQueryFilters extends QueryFilters<any> = QueryFilters, >(filters: TQueryFilters): Array<[QueryKey, TQueryFnData | undefined]> { return this.#queryCache.findAll(filters).map(({ queryKey, state }) => { const data = state.data as TQueryFnData | undefined return [queryKey, data] }) } /** * Synchronous way to immediately update a query's cached data. If the updater (or the value * passed) resolves to `undefined`, the cache is left untouched and no query is created; * otherwise, if the query does not exist yet, it will be created. To update multiple queries * at once by partially matching query keys, use {@link QueryClient#setQueriesData} instead. * * Updates must be performed immutably: do not mutate `oldData`, or data previously retrieved * via {@link QueryClient#getQueryData}, in place. * @param queryKey - The query key to set data for. * @param updater - Either the new data, or a function that receives the current data (which * may be `undefined`) and returns the new data. * @param options - Set `updatedAt` to override the timestamp the written data is recorded with. * @returns The data that was written, or `undefined` if the updater returned `undefined` — in that case * the write is skipped and the cache is left unchanged. * @example * ```ts * queryClient.setQueryData(['posts'], newPosts) * * // Or, using an updater function that receives the current data: * queryClient.setQueryData(['posts'], (oldPosts) => [...oldPosts, newPost]) * ``` */ setQueryData< TQueryFnData = unknown, TTaggedQueryKey extends QueryKey = QueryKey, TInferredQueryFnData = InferDataFromTag<TQueryFnData, TTaggedQueryKey>, >( queryKey: TTaggedQueryKey, updater: Updater< InferDataFromTag<NoInfer<TQueryFnData>, TTaggedQueryKey> | undefined, NoInfer<TInferredQueryFnData> | undefined >, options?: SetDataOptions, ): NoInfer<TInferredQueryFnData> | undefined { const defaultedOptions = this.defaultQueryOptions< any, any, unknown, any, QueryKey >({ queryKey }) const query = this.#queryCache.get< InferDataFromTag<NoInfer<TQueryFnData>, TTaggedQueryKey> >(defaultedOptions.queryHash) const prevData = query?.state.data const data = functionalUpdate(updater, prevData) if (data === undefined) { return undefined } return this.#queryCache .build(this, defaultedOptions) .setData(data, { ...options, manual: true }) } /** * Synchronous way to immediately update the cached data of multiple queries at once, using * filters or partial query key matching. Only queries that already exist and match the given * filters are updated; no new cache entries are created. Internally this calls * {@link QueryClient#setQueryData} for each matching query. * @param filters - The filters that select which existing queries to update. * @param updater - Either the new data, or a function that receives each matched query's current * data (which may be `undefined`) and returns the new data. * @param options - Set `updatedAt` to override the timestamp the written data is recorded with. * @returns One `[queryKey, data]` tuple per matched query, in the same shape and with the same * `undefined` case as {@link QueryClient#setQueryData}. * @example * ```ts * queryClient.setQueriesData({ queryKey: ['posts'] }, (oldPosts) => * oldPosts ? oldPosts.filter((post) => post.id !== deletedId) : oldPosts, * ) * ``` */ setQueriesData< TQueryFnData, TQueryFilters extends QueryFilters<any> = QueryFilters, >( filters: TQueryFilters, updater: Updater< NoInfer<TQueryFnData> | undefined, NoInfer<TQueryFnData> | undefined >, options?: SetDataOptions, ): Array<[QueryKey, TQueryFnData | undefined]> { return notifyManager.batch(() => this.#queryCache .findAll(filters) .map(({ queryKey }) => [ queryKey, this.setQueryData<TQueryFnData>(queryKey, updater, options), ]), ) } /** * Imperative (non-reactive) way to retrieve an existing query's state. If the query does not * exist, `undefined` is returned. * @param queryKey - The query key of the query to read. * @returns The query's state, or `undefined` if no query with this key exists. * @example * ```ts * const state = queryClient.getQueryState(['posts']) * console.log(state?.dataUpdatedAt) * ``` */ getQueryState< TQueryFnData = unknown, TError = DefaultError, TTaggedQueryKey extends QueryKey = QueryKey, TInferredQueryFnData = InferDataFromTag<TQueryFnData, TTaggedQueryKey>, TInferredError = InferErrorFromTag<TError, TTaggedQueryKey>, >( queryKey: TTaggedQueryKey, ): QueryState<TInferredQueryFnData, TInferredError> | undefined { const options = this.defaultQueryOptions({ queryKey }) return this.#queryCache.get<TInferredQueryFnData, TInferredError>( options.queryHash, )?.state } /** * Removes queries from the cache that match the given filters. Unlike * {@link QueryClient#invalidateQueries} or {@link QueryClient#refetchQueries}, this removes * matching queries from the cache instead of refetching them. Without filters, every query in * the cache is removed. * @param filters - The filters that select which queries to remove. Without filters, every query * is removed. * @example * ```ts * queryClient.removeQueries({ queryKey: ['posts'], exact: true }) * ``` */ removeQueries<TTaggedQueryKey extends QueryKey = QueryKey>( filters?: QueryFilters<TTaggedQueryKey>, ): void { const queryCache = this.#queryCache notifyManager.batch(() => { queryCache.findAll(filters).forEach((query) => { queryCache.remove(query) }) }) } /** * Resets queries matching the given filters back to their initial state (e.g. any * `initialData`), notifying subscribers rather than removing them. Active queries among the * matched set are then refetched, and the returned promise resolves once that refetch settles. * @param filters - The filters that select which queries to reset. Without filters, every query * is reset. * @param options - Passed to the refetch of the active matched queries, e.g. `cancelRefetch` and * `throwOnError`. * @returns A promise that resolves once the refetch of the active matched queries settles. * @example * ```ts * await queryClient.resetQueries({ queryKey: ['posts'], exact: true }) * ``` */ resetQueries<TTaggedQueryKey extends QueryKey = QueryKey>( filters?: QueryFilters<TTaggedQueryKey>, options?: ResetOptions, ): Promise<void> { const queryCache = this.#queryCache return notifyManager.batch(() => { const matched = queryCache.findAll(filters) const queriesToRefetch = new Set(matched) matched.forEach((query) => { query.reset() }) return this.refetchQueries( { type: 'active', predicate: (query) => queriesToRefetch.has(query), }, options, ) }) } /** * Cancels outgoing fetches for queries matching the given filters. Most useful when performing * optimistic updates, since any outgoing refetch that resolves afterwards would otherwise * overwrite the optimistic update. By default (`revert: true`), a cancelled query's data is * reverted to its state before the outgoing fetch started. * * The returned promise never rejects, even if individual cancellations fail. * @param filters - The filters that select which queries to cancel. Without filters, every query * is cancelled. * @param cancelOptions - Passed to each matched query's cancellation. `revert` defaults to * `true`. * @returns A promise that resolves once every cancellation has settled. * @example * ```ts * await queryClient.cancelQueries({ queryKey: ['posts'], exact: true }) * ``` */ cancelQueries<TTaggedQueryKey extends QueryKey = QueryKey>( filters?: QueryFilters<TTaggedQueryKey>, cancelOptions: CancelOptions = {}, ): Promise<void> { const defaultedCancelOptions = { revert: true, ...cancelOptions } const promises = notifyManager.batch(() => this.#queryCache .findAll(filters) .map((query) => query.cancel(defaultedCancelOptions)), ) return Promise.all(promises).then(noop).catch(noop) } /** * Marks queries matching the given filters as invalidated. Unlike * {@link QueryClient#removeQueries}, invalidated queries stay in the cache. * * Unless `filters.refetchType` is `'none'`, matching queries are then refetched via * {@link QueryClient#refetchQueries}, using `filters.refetchType` if set, otherwise * `filters.type`, otherwise `'active'`. * @param filters - The filters that select which queries to invalidate, plus `refetchType` to * control which of them to refetch afterwards. Without filters, every query is invalidated. * @param options - Passed to {@link QueryClient#refetchQueries}, e.g. `cancelRefetch` and * `throwOnError`. * @returns A promise that resolves once the refetch settles, or immediately if `refetchType` is * `'none'`. * @example * ```ts * await queryClient.invalidateQueries({ queryKey: ['posts'], refetchType: 'active' }) * ``` */ invalidateQueries<TTaggedQueryKey extends QueryKey = QueryKey>( filters?: InvalidateQueryFilters<TTaggedQueryKey>, options: InvalidateOptions = {}, ): Promise<void> { return notifyManager.batch(() => { this.#queryCache.findAll(filters).forEach((query) => { query.invalidate() }) if (filters?.refetchType === 'none') { return Promise.resolve() } return this.refetchQueries( { ...filters, type: filters?.refetchType ?? filters?.type ?? 'active', }, options, ) }) } /** * Refetches queries matching the given filters, regardless of whether they are stale. Without * filters, every query in the cache is refetched. Queries that are disabled, or static (only * have observers with a static `staleTime`), are never refetched. * * By default (`cancelRefetch: true`), a currently running fetch is cancelled before the new * one starts. The returned promise resolves once all matching queries have settled; it does * not reject on individual query failures unless `throwOnError` is set. * @param filters - The filters that select which queries to refetch. Without filters, every query * in the cache is included. * @param options - Set `cancelRefetch` to `false` to keep a running fetch instead of cancelling * it, and `throwOnError` to `true` to reject when a refetch fails. * @returns A promise that resolves once every matched query has settled. * @example * ```ts * // refetch all active queries partially matching a query key: * await queryClient.refetchQueries({ queryKey: ['posts'], type: 'active' }) * ``` */ refetchQueries<TTaggedQueryKey extends QueryKey = QueryKey>( filters?: RefetchQueryFilters<TTaggedQueryKey>, options: RefetchOptions = {}, ): Promise<void> { const fetchOptions = { ...options, cancelRefetch: options.cancelRefetch ?? true, } const promises = notifyManager.batch(() => this.#queryCache .findAll(filters) .filter((query) => !query.isDisabled() && !query.isStatic()) .map((query) => { let promise = query.fetch(undefined, fetchOptions) if (!fetchOptions.throwOnError) { promise = promise.catch(noop) } return query.state.fetchStatus === 'paused' ? Promise.resolve() : promise }), ) return Promise.all(promises).then(noop) } /** * Asynchronous method to fetch and cache a query, resolving with the data or throwing with * the error. * * If the query already exists in the cache and its data is not stale (per the given * `staleTime`), the cached data is returned without fetching. Otherwise, the query is fetched * and the promise resolves once the fetch settles. If a `select` function is provided, it is * applied to the data in both cases (cached or freshly fetched) before it is returned. * * Unlike a reactive observer, retries are disabled by default here (`retry: false`) unless * explicitly configured, since there is no component to catch a thrown error and retry through * re-render. * * The accepted options are `QueryObserverOptions` minus the fields that only make sense for a * reactive observer — `enabled`, `refetchInterval`, `refetchIntervalInBackground`, * `refetchOnWindowFocus`, `refetchOnReconnect`, `refetchOnMount`, `retryOnMount`, * `notifyOnChangeProps`, `throwOnError`, `suspense`, and `placeholderData` are not part of this * method's options. * * This method replaces the deprecated `fetchQuery`, and — combined with * `{ staleTime: 'static' }` — the deprecated `ensureQueryData`. * @param options - The query options, including the `queryKey` and the `queryFn` used if the * query needs to fetch. * @returns A promise that resolves to the data, or to the result of `select` if provided. It * rejects with the error from the fetch or from `select`. * @example * ```ts * try { * const data = await queryClient.query({ queryKey, queryFn, staleTime: 10000 }) * } catch (error) { * console.log(error) * } * ``` */ async query< TQueryFnData, TError = DefaultError, TData = TQueryFnData, TQueryData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, TPageParam = never, >( options: QueryExecuteOptions< TQueryFnData, TError, TData, TQueryData, TQueryKey, TPageParam >, ): Promise<TData> { const defaultedOptions = this.defaultQueryOptions(options) // https://github.com/tannerlinsley/react-query/issues/652 if (defaultedOptions.retry === undefined) { defaultedOptions.retry = false } const query = this.#queryCache.build(this, defaultedOptions) const isStale = query.isStaleByTime( resolveQueryValue(defaultedOptions.staleTime, query), ) const queryData = isStale ? await query.fetch(defaultedOptions) : (query.state.data as TQueryData) const select = defaultedOptions.select if (select) { return select(queryData) } return queryData as unknown as TData } /** * Fetches and caches a query, or returns its cached data if it isn't stale. Unlike in observers, * the fetch isn't retried unless `retry` is set. * @param options - The query options, including the `queryKey` and the `queryFn` used if the * query needs to fetch. * @returns A promise that resolves to the cached or fetched data, or rejects with the fetch * error. * @deprecated Use queryClient.query(options) instead. This method will be removed in the next major version. */ fetchQuery< TQueryFnData, TError = DefaultError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, TPageParam = never, >( options: FetchQueryOptions< TQueryFnData, TError, TData, TQueryKey, TPageParam >, ): Promise<TData> { const defaultedOptions = this.defaultQueryOptions(options) // https://github.com/tannerlinsley/react-query/issues/652 if (defaultedOptions.retry === undefined) { defaultedOptions.retry = false } const query = this.#queryCache.build(this, defaultedOptions) return query.isStaleByTime( resolveQueryValue(defaultedOptions.staleTime, query), ) ? query.fetch(defaultedOptions) : Promise.resolve(query.state.data as TData) } /** * Fetches and caches a query like {@link QueryClient#fetchQuery}, but ignores the result and any * error. * @param options - The query options, including the `queryKey` and the `queryFn` used if the * query needs to fetch. * @returns A promise that resolves once the fetch settles. It never rejects. * @deprecated Use queryClient.query(options) instead. You can swallow errors with `.catch(noop)`. This method will be removed in the next major version. */ prefetchQuery< TQueryFnData = unknown, TError = DefaultError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( options: FetchQueryOptions<TQueryFnData, TError, TData, TQueryKey>, ): Promise<void> { // oxlint-disable-next-line tanstack-query/no-restricted-syntax return this.fetchQuery(options).then(noop).catch(noop) } /** * Asynchronous method to fetch and cache an infinite query, resolving with an * {@link InfiniteData} object or throwing with the error. * * Behaves like {@link QueryClient#query}, accepting the same options (minus * `initialPageParam`), plus the required `initialPageParam`, and an optional `pages` / * `getNextPageParam` pair used to refetch a fixed number of pages from the start. * * This method replaces the deprecated `fetchInfiniteQuery`, and — combined with * `{ staleTime: 'static' }` — the deprecated `ensureInfiniteQueryData`. * @param options - The infinite query options, including the `queryKey`, the `queryFn`, and the * `initialPageParam`. * @returns A promise that resolves to the {@link InfiniteData}, or to the result of `select` if * provided. It rejects with the error from the fetch or from `select`. * @example * ```ts * try { * const data = await queryClient.infiniteQuery({ queryKey, queryFn, initialPageParam: 0 }) * console.log(data.pages) * } catch (error) { * console.log(error) * } * ``` */ infiniteQuery< TQueryFnData, TError = DefaultError, TData = InfiniteData<TQueryFnData>, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, >( options: InfiniteQueryExecuteOptions< TQueryFnData, TError, TData, TQueryKey, TPageParam >, ): Promise< Array<TData> extends Array<InfiniteData<TQueryFnData>> ? InfiniteData<TQueryFnData, TPageParam> : TData > { options._type = 'infinite' return this.query(options as any) } /** * Fetches and caches an infinite query, or returns its cached data if it isn't stale. * @param options - The infinite query options, including the `queryKey`, the `queryFn`, and the * `initialPageParam`. * @returns A promise that resolves to the cached or fetched {@link InfiniteData}, or rejects with * the fetch error. * @deprecated Use queryClient.infiniteQuery(options) instead. This method will be removed in the next major version. */ fetchInfiniteQuery< TQueryFnData, TError = DefaultError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, >( options: FetchInfiniteQueryOptions< TQueryFnData, TError, TData, TQueryKey, TPageParam >, ): Promise<InfiniteData<TData, TPageParam>> { options._type = 'infinite' // oxlint-disable-next-line tanstack-query/no-restricted-syntax return this.fetchQuery(options as any) } /** * Fetches and caches an infinite query like {@link QueryClient#fetchInfiniteQuery}, but ignores * the result and any error. * @param options - The infinite query options, including the `queryKey`, the `queryFn`, and the * `initialPageParam`. * @returns A promise that resolves once the fetch settles. It never rejects. * @deprecated Use queryClient.infiniteQuery(options) instead. You can swallow errors with `.catch(noop)`. This method will be removed in the next major version. */ prefetchInfiniteQuery< TQueryFnData, TError = DefaultError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, >( options: FetchInfiniteQueryOptions< TQueryFnData, TError, TData, TQueryKey, TPageParam >, ): Promise<void> { // oxlint-disable-next-line tanstack-query/no-restricted-syntax return this.fetchInfiniteQuery(options).then(noop).catch(noop) } /** * Returns the cached data of an infinite query, or fetches it if there is none. With * `revalidateIfStale`, stale cached data is also refetched in the background. * @param options - The infinite query options. If the query has no cached data yet, it is fetched * with these options. * @returns A promise that resolves to the cached {@link InfiniteData}, or to the fetched data if * nothing was cached yet. * @deprecated Use queryClient.infiniteQuery({ ...options, staleTime: 'static' }) instead. This method will be removed in the next major version. */ ensureInfiniteQueryData< TQueryFnData, TError = DefaultError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, >( options: EnsureInfiniteQueryDataOptions< TQueryFnData, TError, TData, TQueryKey, TPageParam >, ): Promise<InfiniteData<TData, TPageParam>> { options._type = 'infinite' // oxlint-disable-next-line tanstack-query/no-restricted-syntax return this.ensureQueryData(options as any) } /** * Resumes mutations that were paused because there was no network connection. Does nothing * (resolving immediately) if the client is currently offline. * @returns A promise that resolves once the resumed mutations have settled. * @example * ```ts * import { QueryClient } from '@tanstack/query-core' * * const queryClient = new QueryClient() * await queryClient.resumePausedMutations() * ``` */ resumePausedMutations(): Promise<unknown> { if (onlineManager.isOnline()) { return this.#mutationCache.resumePausedMutations() } return Promise.resolve() } /** * Returns the query cache this client is connected to. * @returns The {@link QueryCache} instance. * @example * ```ts * import { QueryClient } from '@tanstack/query-core' * * const queryClient = new QueryClient() * const queryCache = queryClient.getQueryCache() * const queries = queryCache.findAll({ queryKey: ['posts'] }) * ``` */ getQueryCache(): QueryCache { return this.#queryCache } /** * Returns the mutation cache this client is connected to. * @returns The {@link MutationCache} instance. * @example * ```ts * import { QueryClient } from '@tanstack/query-core' * * const queryClient = new QueryClient() * const mutationCache = queryClient.getMutationCache() * const mutations = mutationCache.findAll({ status: 'pending' }) * ``` */ getMutationCache(): MutationCache { return this.#mutationCache } /** * Returns the default options that were set when creating the client, or via * {@link QueryClient#setDefaultOptions}. * @returns The client's current default options. * @example * ```ts * import { QueryClient } from '@tanstack/query-core' * * const queryClient = new QueryClient() * const defaultOptions = queryClient.getDefaultOptions() * ``` */ getDefaultOptions(): DefaultOptions { return this.#defaultOptions } /** * Dynamically sets the default options for this client, overwriting any previously defined * default options. * @param options - The new default options for queries and mutations. * @see {@link QueryClient#getDefaultOptions} * @example * ```ts * import { QueryClient } from '@tanstack/query-core' * * const queryClient = new QueryClient() * queryClient.setDefaultOptions({ * queries: { * staleTime: Infinity, * }, * }) * ``` */ setDefaultOptions(options: DefaultOptions): void { this.#defaultOptions = options } /** * Sets default options for queries whose query key partially matches the given `queryKey`. * * If several registered query defaults match a given query key, they are merged together in * registration order by {@link QueryClient#getQueryDefaults}, so register defaults from the * most generic key to the least generic one — more specific defaults should be registered * after more generic ones so they take precedence. * @param queryKey - The query key that query keys are partially matched against. * @param options - The default options applied to matching queries. * @example * ```ts * queryClient.setQueryDefaults(['posts'], { queryFn: fetchPosts }) * * await queryClient.query({ queryKey: ['posts'] }) * ``` */ setQueryDefaults< TQueryFnData = unknown, TError = DefaultError, TData = TQueryFnData, TQueryData = TQueryFnData, >( queryKey: QueryKey, options: Partial< OmitKeyof< QueryObserverOptions<TQueryFnData, TError, TData, TQueryData>, 'queryKey' > >, ): void { this.#queryDefaults.set(hashKey(queryKey), { queryKey, defaultOptions: options, }) } /** * Returns the default options registered for queries whose query key partially matches the * given `queryKey`, via {@link QueryClient#setQueryDefaults}. If multiple registered defaults * match, they are merged together in registration order. * @param queryKey - The query key to look up registered defaults for. * @returns The merged default options of every registration that matches, or an empty object if * none match. * @example * ```ts * const defaultOptions = queryClient.getQueryDefaults(['posts']) * ``` */ getQueryDefaults( queryKey: QueryKey, ): OmitKeyof<QueryObserverOptions<any, any, any, any, any>, 'queryKey'> { const defaults = [...this.#queryDefaults.values()] const result: OmitKeyof< QueryObserverOptions<any, any, any, any, any>, 'queryKey' > = {} defaults.forEach((queryDefault) => { if (partialMatchKey(queryKey, queryDefault.queryKey)) { Object.assign(result, queryDefault.defaultOptions) } }) return result } /** * Sets default options for mutations whose mutation key partially matches the given * `mutationKey`. As with {@link QueryClient#setQueryDefaults}, the order of registration * matters when several registered defaults match the same mutation key. * @param mutationKey - The mutation key that mutation keys are partially matched against. * @param options - The default options applied to matching mutations. * @see {@link QueryClient#getMutationDefaults} * @example * ```ts * queryClient.setMutationDefaults(['addPost'], { mutationFn: addPost }) * ``` */ setMutationDefaults< TData = unknown, TError = DefaultError, TVariables = void, TOnMutateResult = unknown, >( mutationKey: MutationKey, options: OmitKeyof< MutationObserverOptions<TData, TError, TVariables, TOnMutateResult>, 'mutationKey' >, ): void { this.#mutationDefaults.set(hashKey(mutationKey), { mutationKey, defaultOptions: options, }) } /** * Returns the default options registered for mutations whose mutation key partially matches * the given `mutationKey`, via {@link QueryClient#setMutationDefaults}. If multiple registered * defaults match, they are merged together in registration order. * @param mutationKey - The mutation key to look up registered defaults for. * @returns The merged default options of every registration that matches, or an empty object if * none match. * @example * ```ts * const defaultOptions = queryClient.getMutationDefaults(['addPost']) * ``` */ getMutationDefaults( mutationKey: MutationKey, ): OmitKeyof<MutationObserverOptions<any, any, any, any>, 'mutationKey'> { const defaults = [...this.#mutationDefaults.values()] const result: OmitKeyof< MutationObserverOptions<any, any, any, any>, 'mutationKey' > = {} defaults.forEach((queryDefault) => { if (partialMatchKey(mutationKey, queryDefault.mutationKey)) { Object.assign(result, queryDefault.defaultOptions) } }) return result } /** * Called by framework adapters (e.g. inside `useQuery`) to resolve the options passed by the * caller into their final, defaulted form: merging the client's own `defaultOptions.queries`, * then `queryClient.setQueryDefaults` for the given `queryKey`, then the caller's options on * top. A no-op if the options are already defaulted (`_defaulted: true`). * @param options - The query options passed by the caller. * @returns The defaulted options, with `queryHash` and dependent defaults (e.g. * `refetchOnReconnect`) filled in. */ defaultQueryOptions< TQueryFnData = unknown, TError = DefaultError, TData = TQueryFnData, TQueryData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, TPageParam = never, >( options: | QueryObserverOptions< TQueryFnData, TError, TData, TQueryData, TQueryKey, TPageParam > | DefaultedQueryObserverOptions< TQueryFnData, TError, TData, TQueryData, TQueryKey >, ): DefaultedQueryObserverOptions< TQueryFnData, TError, TData, TQueryData, TQueryKey > { if (options._defaulted) { return options as DefaultedQueryObserverOptions< TQueryFnData, TError, TData, TQueryData, TQueryKey > } const defaultedOptions = { ...this.#defaultOptions.queries, ...this.getQueryDefaults(options.queryKey), ...options, _defaulted: true, } if (!defaultedOptions.queryHash) { defaultedOptions.queryHash = hashQueryKeyByOptions( defaultedOptions.queryKey, defaultedOptions, ) } // dependent default values if (defaultedOptions.refetchOnReconnect === undefined) { defaultedOptions.refetchOnReconnect = defaultedOptions.networkMode !== 'always' } if (defaultedOptions.throwOnError === undefined) { defaultedOptions.throwOnError = !!defaultedOptions.suspense } if (!defaultedOptions.networkMode && defaultedOptions.persister) { defaultedOptions.networkMode = 'offlineFirst' } if (defaultedOptions.queryFn === skipToken) { defaultedOptions.enabled = false } return defaultedOptions as DefaultedQueryObserverOptions< TQueryFnData, TError, TData, TQueryData, TQueryKey > } /** * The mutation counterpart of {@link QueryClient#defaultQueryOptions}. Called by framework * adapters (e.g. inside `useMutation`) to merge the client's `defaultOptions.mutations`, then * `queryClient.setMutationDefaults` for the given `mutationKey`, then the caller's options on * top. A no-op if the options are already defaulted (`_defaulted: true`). * @param options - The mutation options passed by the caller. * @returns The defaulted options. */ defaultMutationOptions<T extends MutationOptions<any, any, any, any>>( options?: T, ): T { if (options?._defaulted) { return options } return { ...this.#defaultOptions.mutations, ...(options?.mutationKey && this.getMutationDefaults(options.mutationKey)), ...options, _defaulted: true, } as T } /** * Clears both the query cache and the mutation cache this client is connected to. * @example * ```ts * import { QueryClient } from '@tanstack/query-core' * * const queryClient = new QueryClient() * queryClient.clear() * ``` */ clear(): void { this.#queryCache.clear() this.#mutationCache.clear() } }