diff --git a/.changeset/fix-query-collection-default-options.md b/.changeset/fix-query-collection-default-options.md new file mode 100644 index 000000000..70edd58f2 --- /dev/null +++ b/.changeset/fix-query-collection-default-options.md @@ -0,0 +1,29 @@ +--- +"@tanstack/query-db-collection": patch +--- + +Fix queryCollectionOptions to respect QueryClient defaultOptions when not overridden + +Previously, when creating a QueryClient with defaultOptions (e.g., staleTime, retry, refetchOnWindowFocus), these options were ignored by queryCollectionOptions unless explicitly specified again in the collection config. This required duplicating configuration and prevented users from setting global defaults. + +Now, queryCollectionOptions properly respects the QueryClient's defaultOptions as fallbacks. Options explicitly provided in queryCollectionOptions will still override the defaults. + +Example - this now works as expected: + +```typescript +const dbQueryClient = new QueryClient({ + defaultOptions: { + queries: { + refetchOnWindowFocus: false, + staleTime: Infinity, + }, + }, +}) + +queryCollectionOptions({ + id: "wallet-accounts", + queryKey: ["wallet-accounts"], + queryClient: dbQueryClient, + // staleTime: Infinity is now inherited from defaultOptions +}) +``` diff --git a/packages/query-db-collection/src/query.ts b/packages/query-db-collection/src/query.ts index 2d53105cc..9512bf47c 100644 --- a/packages/query-db-collection/src/query.ts +++ b/packages/query-db-collection/src/query.ts @@ -433,14 +433,15 @@ export function queryCollectionOptions( > = { queryKey: queryKey, queryFn: queryFn, - meta: meta, - enabled: enabled, - refetchInterval: refetchInterval, - retry: retry, - retryDelay: retryDelay, - staleTime: staleTime, structuralSharing: true, notifyOnChangeProps: `all`, + // Only include options that are explicitly defined to allow QueryClient defaultOptions to be used + ...(meta !== undefined && { meta }), + ...(enabled !== undefined && { enabled }), + ...(refetchInterval !== undefined && { refetchInterval }), + ...(retry !== undefined && { retry }), + ...(retryDelay !== undefined && { retryDelay }), + ...(staleTime !== undefined && { staleTime }), } const localObserver = new QueryObserver< diff --git a/packages/query-db-collection/tests/query.test.ts b/packages/query-db-collection/tests/query.test.ts index b87caf67c..a16f97906 100644 --- a/packages/query-db-collection/tests/query.test.ts +++ b/packages/query-db-collection/tests/query.test.ts @@ -2340,4 +2340,141 @@ describe(`QueryCollection`, () => { expect(collection.size).toBe(items.length) }) }) + + describe(`QueryClient defaultOptions`, () => { + it(`should respect defaultOptions from QueryClient when not overridden`, async () => { + // Create a QueryClient with custom defaultOptions + const customQueryClient = new QueryClient({ + defaultOptions: { + queries: { + staleTime: 10000, // 10 seconds + retry: 2, + refetchOnWindowFocus: false, + }, + }, + }) + + const queryKey = [`defaultOptionsTest`] + const items: Array = [{ id: `1`, name: `Item 1` }] + const queryFn = vi.fn().mockResolvedValue(items) + + // Create a collection without specifying staleTime or retry + const config: QueryCollectionConfig = { + id: `defaultOptionsTest`, + queryClient: customQueryClient, + queryKey, + queryFn, + getKey, + startSync: true, + } + + const options = queryCollectionOptions(config) + const collection = createCollection(options) + + await vi.waitFor(() => { + expect(collection.status).toBe(`ready`) + }) + + // Verify queryFn was called once + expect(queryFn).toHaveBeenCalledTimes(1) + + // Verify the query has the correct staleTime from defaultOptions + const query = customQueryClient.getQueryCache().find({ queryKey }) + expect((query?.options as any).staleTime).toBe(10000) + + // Clean up + customQueryClient.clear() + }) + + it(`should override defaultOptions when explicitly provided in queryCollectionOptions`, async () => { + // Create a QueryClient with custom defaultOptions + const customQueryClient = new QueryClient({ + defaultOptions: { + queries: { + staleTime: 10000, // 10 seconds default + retry: 2, + }, + }, + }) + + const queryKey = [`overrideOptionsTest`] + const items: Array = [{ id: `1`, name: `Item 1` }] + const queryFn = vi.fn().mockResolvedValue(items) + + // Create a collection WITH explicit staleTime override + const config: QueryCollectionConfig = { + id: `overrideOptionsTest`, + queryClient: customQueryClient, + queryKey, + queryFn, + getKey, + startSync: true, + staleTime: 100, // Override to 100ms + } + + const options = queryCollectionOptions(config) + const collection = createCollection(options) + + await vi.waitFor(() => { + expect(collection.status).toBe(`ready`) + }) + + // Verify the query uses the overridden staleTime (100ms), not the default (10000ms) + const query = customQueryClient.getQueryCache().find({ queryKey }) + expect((query?.options as any).staleTime).toBe(100) + + // Clean up + customQueryClient.clear() + }) + + it(`should use retry from QueryClient defaultOptions when not overridden`, async () => { + let callCount = 0 + // Create a QueryClient with custom retry defaultOption + const customQueryClient = new QueryClient({ + defaultOptions: { + queries: { + retry: 2, // Retry 2 times + retryDelay: 1, // 1ms delay for fast test + }, + }, + }) + + const queryKey = [`retryDefaultOptionsTest`] + const queryFn = vi.fn().mockImplementation(() => { + callCount++ + // Fail on first 2 attempts, succeed on 3rd + if (callCount <= 2) { + return Promise.reject(new Error(`Attempt ${callCount} failed`)) + } + return Promise.resolve([{ id: `1`, name: `Item 1` }]) + }) + + // Create a collection without specifying retry + const config: QueryCollectionConfig = { + id: `retryDefaultOptionsTest`, + queryClient: customQueryClient, + queryKey, + queryFn, + getKey, + startSync: true, + } + + const options = queryCollectionOptions(config) + const collection = createCollection(options) + + // Wait for the query to eventually succeed (after retries) + await vi.waitFor( + () => { + expect(collection.status).toBe(`ready`) + }, + { timeout: 2000 } + ) + + // Should have called queryFn 3 times (initial + 2 retries) + expect(callCount).toBe(3) + + // Clean up + customQueryClient.clear() + }) + }) })