diff --git a/packages/query-core/src/queryClient.ts b/packages/query-core/src/queryClient.ts index 7673ee1f56..0585aa4799 100644 --- a/packages/query-core/src/queryClient.ts +++ b/packages/query-core/src/queryClient.ts @@ -56,8 +56,8 @@ export class QueryClient { #mutationCache: MutationCache #logger: Logger #defaultOptions: DefaultOptions - #queryDefaults: QueryDefaults[] - #mutationDefaults: MutationDefaults[] + #queryDefaults: Map + #mutationDefaults: Map #mountCount: number #unsubscribeFocus?: () => void #unsubscribeOnline?: () => void @@ -67,8 +67,8 @@ export class QueryClient { this.#mutationCache = config.mutationCache || new MutationCache() this.#logger = config.logger || defaultLogger this.#defaultOptions = config.defaultOptions || {} - this.#queryDefaults = [] - this.#mutationDefaults = [] + this.#queryDefaults = new Map() + this.#mutationDefaults = new Map() this.#mountCount = 0 if (process.env.NODE_ENV !== 'production' && config.logger) { @@ -366,92 +366,55 @@ export class QueryClient { setQueryDefaults( queryKey: QueryKey, - options: Omit, 'queryKey'>, + options: Partial< + Omit, 'queryKey'> + >, ): void { - const result = this.#queryDefaults.find( - (x) => hashKey(queryKey) === hashKey(x.queryKey), - ) - if (result) { - result.defaultOptions = options - } else { - this.#queryDefaults.push({ queryKey, defaultOptions: options }) - } + this.#queryDefaults.set(hashKey(queryKey), { + queryKey, + defaultOptions: options, + }) } getQueryDefaults( - queryKey?: QueryKey, - ): QueryObserverOptions | undefined { - if (!queryKey) { - return undefined - } + queryKey: QueryKey, + ): QueryObserverOptions { + const defaults = [...this.#queryDefaults.values()] - // Get the first matching defaults - const firstMatchingDefaults = this.#queryDefaults.find((x) => - partialMatchKey(queryKey, x.queryKey), - ) + let result: QueryObserverOptions = {} - // Additional checks and error in dev mode - if (process.env.NODE_ENV !== 'production') { - // Retrieve all matching defaults for the given key - const matchingDefaults = this.#queryDefaults.filter((x) => - partialMatchKey(queryKey, x.queryKey), - ) - // It is ok not having defaults, but it is error prone to have more than 1 default for a given key - if (matchingDefaults.length > 1) { - this.#logger.error( - `[QueryClient] Several query defaults match with key '${JSON.stringify( - queryKey, - )}'. The first matching query defaults are used. Please check how query defaults are registered. Order does matter here. cf. https://react-query.tanstack.com/reference/QueryClient#queryclientsetquerydefaults.`, - ) + defaults.forEach((queryDefault) => { + if (partialMatchKey(queryKey, queryDefault.queryKey)) { + result = { ...result, ...queryDefault.defaultOptions } } - } - - return firstMatchingDefaults?.defaultOptions + }) + return result } setMutationDefaults( mutationKey: MutationKey, - options: MutationObserverOptions, + options: Omit, 'mutationKey'>, ): void { - const result = this.#mutationDefaults.find( - (x) => hashKey(mutationKey) === hashKey(x.mutationKey), - ) - if (result) { - result.defaultOptions = options - } else { - this.#mutationDefaults.push({ mutationKey, defaultOptions: options }) - } + this.#mutationDefaults.set(hashKey(mutationKey), { + mutationKey, + defaultOptions: options, + }) } getMutationDefaults( - mutationKey?: MutationKey, - ): MutationObserverOptions | undefined { - if (!mutationKey) { - return undefined - } + mutationKey: MutationKey, + ): MutationObserverOptions { + const defaults = [...this.#mutationDefaults.values()] - // Get the first matching defaults - const firstMatchingDefaults = this.#mutationDefaults.find((x) => - partialMatchKey(mutationKey, x.mutationKey), - ) + let result: MutationObserverOptions = {} - // Additional checks and error in dev mode - if (process.env.NODE_ENV !== 'production') { - // Retrieve all matching defaults for the given key - const matchingDefaults = this.#mutationDefaults.filter((x) => - partialMatchKey(mutationKey, x.mutationKey), - ) - // It is ok not having defaults, but it is error prone to have more than 1 default for a given key - if (matchingDefaults.length > 1) { - this.#logger.error( - `[QueryClient] Several mutation defaults match with key '${JSON.stringify( - mutationKey, - )}'. The first matching mutation defaults are used. Please check how mutation defaults are registered. Order does matter here. cf. https://react-query.tanstack.com/reference/QueryClient#queryclientsetmutationdefaults.`, - ) + defaults.forEach((queryDefault) => { + if (partialMatchKey(mutationKey, queryDefault.mutationKey)) { + result = { ...result, ...queryDefault.defaultOptions } } - } + }) - return firstMatchingDefaults?.defaultOptions + return result } defaultQueryOptions< @@ -489,12 +452,12 @@ export class QueryClient { const defaultedOptions = { ...this.#defaultOptions.queries, - ...this.getQueryDefaults(options?.queryKey), + ...(options?.queryKey && this.getQueryDefaults(options.queryKey)), ...options, _defaulted: true, } - if (!defaultedOptions.queryHash && defaultedOptions.queryKey) { + if (!defaultedOptions.queryHash) { defaultedOptions.queryHash = hashQueryKeyByOptions( defaultedOptions.queryKey, defaultedOptions, @@ -527,7 +490,8 @@ export class QueryClient { } return { ...this.#defaultOptions.mutations, - ...this.getMutationDefaults(options?.mutationKey), + ...(options?.mutationKey && + this.getMutationDefaults(options.mutationKey)), ...options, _defaulted: true, } as T diff --git a/packages/query-core/src/tests/queryClient.test.tsx b/packages/query-core/src/tests/queryClient.test.tsx index 648cd9605b..bb7906e24b 100644 --- a/packages/query-core/src/tests/queryClient.test.tsx +++ b/packages/query-core/src/tests/queryClient.test.tsx @@ -126,123 +126,17 @@ describe('queryClient', () => { expect(queryClient.getQueryDefaults(key)).toMatchObject(queryOptions2) }) - test('should warn in dev if several query defaults match a given key', () => { - // Check discussion here: https://github.com/tannerlinsley/react-query/discussions/3199 - const keyABCD = [ - { - a: 'a', - b: 'b', - c: 'c', - d: 'd', - }, - ] - - // The key below "contains" keyABCD => it is more generic - const keyABC = [ - { - a: 'a', - b: 'b', - c: 'c', - }, - ] - - // The defaults for query matching key "ABCD" (least generic) - const defaultsOfABCD = { - queryFn: function ABCDQueryFn() { - return 'ABCD' - }, - } - - // The defaults for query matching key "ABC" (most generic) - const defaultsOfABC = { - queryFn: function ABCQueryFn() { - return 'ABC' - }, - } - - // No defaults, no warning - const noDefaults = queryClient.getQueryDefaults(keyABCD) - expect(noDefaults).toBeUndefined() - expect(mockLogger.error).toHaveBeenCalledTimes(1) - - // If defaults for key ABCD are registered **before** the ones of key ABC (more generic)… - queryClient.setQueryDefaults(keyABCD, defaultsOfABCD) - queryClient.setQueryDefaults(keyABC, defaultsOfABC) - // … then the "good" defaults are retrieved: we get the ones for key "ABCD" - const goodDefaults = queryClient.getQueryDefaults(keyABCD) - expect(goodDefaults).toBe(defaultsOfABCD) - // The warning is still raised since several defaults are matching - expect(mockLogger.error).toHaveBeenCalledTimes(2) - - // Let's create another queryClient and change the order of registration - const newQueryClient = createQueryClient() - // The defaults for key ABC (more generic) are registered **before** the ones of key ABCD… - newQueryClient.setQueryDefaults(keyABC, defaultsOfABC) - newQueryClient.setQueryDefaults(keyABCD, defaultsOfABCD) - // … then the "wrong" defaults are retrieved: we get the ones for key "ABC" - const badDefaults = newQueryClient.getQueryDefaults(keyABCD) - expect(badDefaults).not.toBe(defaultsOfABCD) - expect(badDefaults).toBe(defaultsOfABC) - expect(mockLogger.error).toHaveBeenCalledTimes(4) - }) - - test('should warn in dev if several mutation defaults match a given key', () => { - // Check discussion here: https://github.com/tannerlinsley/react-query/discussions/3199 - const keyABCD = [ - { - a: 'a', - b: 'b', - c: 'c', - d: 'd', - }, - ] - - // The key below "contains" keyABCD => it is more generic - const keyABC = [ - { - a: 'a', - b: 'b', - c: 'c', - }, - ] - - // The defaults for mutation matching key "ABCD" (least generic) - const defaultsOfABCD = { - mutationFn: Promise.resolve, - } - - // The defaults for mutation matching key "ABC" (most generic) - const defaultsOfABC = { - mutationFn: Promise.resolve, - } + test('should merge defaultOptions', async () => { + const key = queryKey() - // No defaults, no warning - const noDefaults = queryClient.getMutationDefaults(keyABCD) - expect(noDefaults).toBeUndefined() - expect(mockLogger.error).toHaveBeenNthCalledWith( - 1, - 'Passing a custom logger has been deprecated and will be removed in the next major version.', - ) + queryClient.setQueryDefaults([...key, 'todo'], { suspense: true }) + queryClient.setQueryDefaults([...key, 'todo', 'detail'], { + staleTime: 5000, + }) - // If defaults for key ABCD are registered **before** the ones of key ABC (more generic)… - queryClient.setMutationDefaults(keyABCD, defaultsOfABCD) - queryClient.setMutationDefaults(keyABC, defaultsOfABC) - // … then the "good" defaults are retrieved: we get the ones for key "ABCD" - const goodDefaults = queryClient.getMutationDefaults(keyABCD) - expect(goodDefaults).toBe(defaultsOfABCD) - // The warning is still raised since several defaults are matching - expect(mockLogger.error).toHaveBeenCalledTimes(2) - - // Let's create another queryClient and change the order of registration - const newQueryClient = createQueryClient() - // The defaults for key ABC (more generic) are registered **before** the ones of key ABCD… - newQueryClient.setMutationDefaults(keyABC, defaultsOfABC) - newQueryClient.setMutationDefaults(keyABCD, defaultsOfABCD) - // … then the "wrong" defaults are retrieved: we get the ones for key "ABC" - const badDefaults = newQueryClient.getMutationDefaults(keyABCD) - expect(badDefaults).not.toBe(defaultsOfABCD) - expect(badDefaults).toBe(defaultsOfABC) - expect(mockLogger.error).toHaveBeenCalledTimes(4) + expect( + queryClient.getQueryDefaults([...key, 'todo', 'detail']), + ).toMatchObject({ suspense: true, staleTime: 5000 }) }) }) diff --git a/packages/vue-query/src/queryClient.ts b/packages/vue-query/src/queryClient.ts index 6c4682a9b4..80729c30b4 100644 --- a/packages/vue-query/src/queryClient.ts +++ b/packages/vue-query/src/queryClient.ts @@ -264,8 +264,8 @@ export class QueryClient extends QC { } getQueryDefaults( - queryKey?: MaybeRefDeep, - ): QueryObserverOptions | undefined { + queryKey: MaybeRefDeep, + ): QueryObserverOptions { return super.getQueryDefaults(cloneDeepUnref(queryKey)) } @@ -280,8 +280,8 @@ export class QueryClient extends QC { } getMutationDefaults( - mutationKey?: MaybeRefDeep, - ): MutationObserverOptions | undefined { + mutationKey: MaybeRefDeep, + ): MutationObserverOptions { return super.getMutationDefaults(cloneDeepUnref(mutationKey)) } }