UNPKG

@tanstack/query-core

Version:

The framework agnostic core that powers TanStack Query

240 lines (239 loc) • 7.36 kB
import { hashQueryKeyByOptions, matchQuery } from "./utils.js"; import { Subscribable } from "./subscribable.js"; import { notifyManager } from "./notifyManager.js"; import { Query } from "./query.js"; //#region src/queryCache.ts /** * The `QueryCache` is the storage mechanism for TanStack Query. It stores all the data, meta * information, and state of the queries it contains. * * Normally, you will not interact with the `QueryCache` directly and instead use a `QueryClient` * for a specific cache. You can subscribe to it (inherited from `Subscribable`) to be informed of * safe/known updates to the cache, such as queries being added, removed, or updated — updates made * outside of the cache's own tracked mechanisms (e.g. mutating a query's state object directly) do * not notify subscribers. * @example * ```ts * const unsubscribe = queryCache.subscribe((event) => { * console.log(event.type, event.query) * }) * ``` */ var QueryCache = class extends Subscribable { #queries; constructor(config = {}) { super(); this.config = config; this.#queries = /* @__PURE__ */ new Map(); } /** * Returns the existing `Query` instance for the given options' `queryKey`/`queryHash`, or * builds and adds a new one to the cache if none exists yet. Used by framework adapters and * plugins (e.g. broadcast/persistence) that need to get-or-create a `Query` directly, bypassing * the reactive `QueryObserver` machinery. * @param client - The client the query belongs to, used to default its options. * @param options - The query options, including the `queryKey`. A new query is created with the * options defaulted by {@link QueryClient#defaultQueryOptions}. * @param state - The initial state of a newly created query, e.g. when hydrating. Ignored if the * query already exists. * @returns The existing or newly created query. * @example * ```ts * const queryCache = queryClient.getQueryCache() * * const query = queryCache.build(queryClient, { * queryKey: ['posts'], * queryFn: fetchPosts, * }) * ``` */ build(client, options, state) { const queryKey = options.queryKey; const queryHash = options.queryHash ?? hashQueryKeyByOptions(queryKey, options); let query = this.get(queryHash); if (!query) { query = new Query({ client, queryKey, queryHash, options: client.defaultQueryOptions(options), state, defaultOptions: client.getQueryDefaults(queryKey) }); this.add(query); } return query; } /** * Adds a query to the cache. * @internal * @param query - The query to add. If a query with the same hash is already cached, nothing * happens. */ add(query) { if (!this.#queries.has(query.queryHash)) { this.#queries.set(query.queryHash, query); this.notify({ type: "added", query }); } } /** * Destroys the given `Query` and removes it from the cache, notifying subscribers with a * `'removed'` event. A no-op if the query is no longer the one currently stored under its hash * (e.g. it was already replaced). Used by plugins (e.g. the broadcast client) that mirror * removals across `QueryCache` instances. * @param query - The query to remove. * @example * ```ts * const queryCache = queryClient.getQueryCache() * const query = queryCache.find({ queryKey: ['posts'] }) * * if (query) { * queryCache.remove(query) * } * ``` */ remove(query) { if (this.#queries.get(query.queryHash) === query) { query.destroy(); this.#queries.delete(query.queryHash); this.notify({ type: "removed", query }); } } /** * Removes all queries from the cache. * @example * ```ts * const queryCache = queryClient.getQueryCache() * * queryCache.clear() * ``` */ clear() { notifyManager.batch(() => { this.getAll().forEach((query) => { this.remove(query); }); }); } /** * Returns the `Query` instance stored under the given `queryHash`, or `undefined` if none * exists. Unlike {@link QueryCache#find}, this looks up by the already-computed hash rather * than by `QueryFilters`. Used by plugins (e.g. broadcast/hydration) that already have a hash * to look up directly. * @param queryHash - The hash of the query to look up. * @returns The query stored under the hash, or `undefined`. * @example * ```ts * const queryCache = queryClient.getQueryCache() * const queryHash = hashKey(['posts']) * * const query = queryCache.get(queryHash) * ``` */ get(queryHash) { return this.#queries.get(queryHash); } /** * Returns all queries within the cache. * @returns Every query in the cache. * @example * ```ts * const queryCache = queryClient.getQueryCache() * * const queries = queryCache.getAll() * ``` */ getAll() { return [...this.#queries.values()]; } /** * A slightly more advanced method that can be used to get an existing query instance from the * cache. This instance not only contains all the state for the query, but all of the instances, * and underlying guts of the query as well. If the query does not exist, `undefined` is * returned. * * This is not typically needed for most applications, but can come in handy when needing more * information about a query in rare scenarios (e.g. looking at `query.state.dataUpdatedAt` to * decide whether a query is fresh enough to be used as an initial value). * @param filters - The filters to match, including the required `queryKey`. `exact` defaults to * `true`. * @returns The first matching query, or `undefined`. * @see {@link QueryCache#findAll} * @example * ```ts * const queryCache = queryClient.getQueryCache() * * const query = queryCache.find({ queryKey: ['posts'] }) * ``` */ find(filters) { const defaultedFilters = { exact: true, ...filters }; return this.getAll().find((query) => matchQuery(defaultedFilters, query)); } /** * An even more advanced method that can be used to get existing query instances from the cache * that partially match a query key. If no queries match, an empty array is returned. * * This is not typically needed for most applications, but can come in handy when needing more * information about queries in rare scenarios. * @param filters - The filters to match. Without filters, every query is returned. * @returns The matching queries. * @see {@link QueryCache#find} * @example * ```ts * const queryCache = queryClient.getQueryCache() * * const queries = queryCache.findAll({ queryKey: ['posts'] }) * ``` */ findAll(filters = {}) { const queries = this.getAll(); return Object.keys(filters).length > 0 ? queries.filter((query) => matchQuery(filters, query)) : queries; } /** * Notifies every cache listener of an event, in one batch. * @internal * @param event - The event passed to every listener. */ notify(event) { notifyManager.batch(() => { this.listeners.forEach((listener) => { listener(event); }); }); } /** * Calls {@link Query#onFocus} on every cached query, in one batch. * @internal */ onFocus() { notifyManager.batch(() => { this.getAll().forEach((query) => { query.onFocus(); }); }); } /** * Calls {@link Query#onOnline} on every cached query, in one batch. * @internal */ onOnline() { notifyManager.batch(() => { this.getAll().forEach((query) => { query.onOnline(); }); }); } }; //#endregion export { QueryCache }; //# sourceMappingURL=queryCache.js.map