Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .changeset/fix-query-collection-default-options.md
Original file line number Diff line number Diff line change
@@ -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
})
```
13 changes: 7 additions & 6 deletions packages/query-db-collection/src/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<
Expand Down
137 changes: 137 additions & 0 deletions packages/query-db-collection/tests/query.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<TestItem> = [{ id: `1`, name: `Item 1` }]
const queryFn = vi.fn().mockResolvedValue(items)

// Create a collection without specifying staleTime or retry
const config: QueryCollectionConfig<TestItem> = {
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<TestItem> = [{ id: `1`, name: `Item 1` }]
const queryFn = vi.fn().mockResolvedValue(items)

// Create a collection WITH explicit staleTime override
const config: QueryCollectionConfig<TestItem> = {
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<TestItem> = {
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()
})
})
})
Loading