Skip to content

Commit b38bd76

Browse files
authored
fix query collection .preload() (#635)
1 parent 26ad333 commit b38bd76

File tree

3 files changed

+78
-4
lines changed

3 files changed

+78
-4
lines changed

.changeset/light-moles-divide.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@tanstack/query-db-collection": patch
3+
---
4+
5+
Fix collection.preload() hanging when called without startSync or subscribers. The QueryObserver now subscribes immediately when sync starts (from preload(), startSync, or first subscriber), while maintaining the staleTime behavior by dynamically unsubscribing when subscriber count drops to zero.

packages/query-db-collection/src/query.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -564,10 +564,9 @@ export function queryCollectionOptions(
564564
}
565565
}
566566

567-
// If startSync=true or there are subscribers to the collection, subscribe to the query straight away
568-
if (config.startSync || collection.subscriberCount > 0) {
569-
subscribeToQuery()
570-
}
567+
// Always subscribe when sync starts (this could be from preload(), startSync config, or first subscriber)
568+
// We'll dynamically unsubscribe/resubscribe based on subscriber count to maintain staleTime behavior
569+
subscribeToQuery()
571570

572571
// Set up event listener for subscriber changes
573572
const unsubscribeFromCollectionEvents = collection.on(

packages/query-db-collection/tests/query.test.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2270,4 +2270,74 @@ describe(`QueryCollection`, () => {
22702270
expect(collection.utils.isError()).toBe(true)
22712271
})
22722272
})
2273+
2274+
describe(`preload()`, () => {
2275+
it(`should resolve preload() even without startSync or subscribers`, async () => {
2276+
const queryKey = [`preload-test`]
2277+
const items: Array<TestItem> = [
2278+
{ id: `1`, name: `Item 1` },
2279+
{ id: `2`, name: `Item 2` },
2280+
]
2281+
2282+
const queryFn = vi.fn().mockResolvedValue(items)
2283+
2284+
const config: QueryCollectionConfig<TestItem> = {
2285+
id: `preload-test`,
2286+
queryClient,
2287+
queryKey,
2288+
queryFn,
2289+
getKey,
2290+
// Note: NOT setting startSync: true
2291+
}
2292+
2293+
const options = queryCollectionOptions(config)
2294+
const collection = createCollection(options)
2295+
2296+
// Collection should be idle initially
2297+
expect(collection.status).toBe(`idle`)
2298+
expect(queryFn).not.toHaveBeenCalled()
2299+
2300+
// Preload should resolve without any subscribers
2301+
await collection.preload()
2302+
2303+
// After preload, collection should be ready and queryFn should have been called
2304+
expect(collection.status).toBe(`ready`)
2305+
expect(queryFn).toHaveBeenCalledTimes(1)
2306+
expect(collection.size).toBe(items.length)
2307+
expect(collection.get(`1`)).toEqual(items[0])
2308+
expect(collection.get(`2`)).toEqual(items[1])
2309+
})
2310+
2311+
it(`should not call queryFn multiple times if preload() is called concurrently`, async () => {
2312+
const queryKey = [`preload-concurrent-test`]
2313+
const items: Array<TestItem> = [{ id: `1`, name: `Item 1` }]
2314+
2315+
const queryFn = vi.fn().mockResolvedValue(items)
2316+
2317+
const config: QueryCollectionConfig<TestItem> = {
2318+
id: `preload-concurrent-test`,
2319+
queryClient,
2320+
queryKey,
2321+
queryFn,
2322+
getKey,
2323+
}
2324+
2325+
const options = queryCollectionOptions(config)
2326+
const collection = createCollection(options)
2327+
2328+
// Call preload() multiple times concurrently
2329+
const promises = [
2330+
collection.preload(),
2331+
collection.preload(),
2332+
collection.preload(),
2333+
]
2334+
2335+
await Promise.all(promises)
2336+
2337+
// queryFn should only be called once despite multiple preload() calls
2338+
expect(queryFn).toHaveBeenCalledTimes(1)
2339+
expect(collection.status).toBe(`ready`)
2340+
expect(collection.size).toBe(items.length)
2341+
})
2342+
})
22732343
})

0 commit comments

Comments
 (0)