From 6af71925b35f07d62564bfb2835cc649f8960d44 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 22 Oct 2025 14:05:36 +0000 Subject: [PATCH 1/2] fix(query-db-collection): respect QueryClient defaultOptions when not overridden MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, queryCollectionOptions would set query options (staleTime, retry, retryDelay, refetchInterval, enabled, meta) to undefined even when not provided in the config. This prevented QueryClient's defaultOptions from being used as fallbacks. The fix conditionally includes these options in the observerOptions object only when they are explicitly defined (not undefined), allowing TanStack Query to properly use defaultOptions from the QueryClient. Added comprehensive tests to verify: 1. defaultOptions are respected when not overridden in queryCollectionOptions 2. explicit options in queryCollectionOptions override defaultOptions 3. retry behavior from defaultOptions works correctly Fixes issue where users couldn't use QueryClient defaultOptions with QueryCollection 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- packages/query-db-collection/src/query.ts | 13 +- .../query-db-collection/tests/query.test.ts | 137 ++++++++++++++++++ 2 files changed, 144 insertions(+), 6 deletions(-) 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() + }) + }) }) From fa1ac865aa7fd299251ea3618a64c1b5761b61a4 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 22 Oct 2025 14:22:29 +0000 Subject: [PATCH 2/2] chore: add changeset for queryCollectionOptions defaultOptions fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../fix-query-collection-default-options.md | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 .changeset/fix-query-collection-default-options.md 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 +}) +```