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
5 changes: 5 additions & 0 deletions .changeset/gold-flowers-rest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@tanstack/query-db-collection": patch
---

fix: race condition creating a collection from a query that has already loaded
11 changes: 9 additions & 2 deletions packages/query-db-collection/src/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,8 @@ export function queryCollectionOptions<
TQueryKey
>(queryClient, observerOptions)

const actualUnsubscribeFn = localObserver.subscribe((result) => {
type UpdateHandler = Parameters<typeof localObserver.subscribe>[0]
const handleUpdate: UpdateHandler = (result) => {
if (result.isSuccess) {
const newItemsArray = result.data

Expand Down Expand Up @@ -575,7 +576,13 @@ export function queryCollectionOptions<
// Mark collection as ready even on error to avoid blocking apps
markReady()
}
})
}

const actualUnsubscribeFn = localObserver.subscribe(handleUpdate)

// Ensure we process any existing query data (QueryObserver doesn't invoke its callback automatically with initial
// state)
handleUpdate(localObserver.getCurrentResult())

return async () => {
actualUnsubscribeFn()
Expand Down
29 changes: 29 additions & 0 deletions packages/query-db-collection/tests/query.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1603,4 +1603,33 @@ describe(`QueryCollection`, () => {
expect(collection.size).toBe(0)
expect(collection.status).toBe(`ready`)
})

it(`should read the state of a query that is already ready`, async () => {
// Populate the query cache, so the query will immediately be loaded
const queryKey = [`raceConditionTest`]
const initialItems: Array<TestItem> = [
{ id: `1`, name: `Cached Item 1` },
{ id: `2`, name: `Cached Item 2` },
]
const queryFn = vi.fn().mockReturnValue(initialItems)
await queryClient.prefetchQuery({ queryKey, queryFn })

// The collection should immediately be ready
const collection = createCollection(
queryCollectionOptions({
id: `test`,
queryClient,
queryKey,
queryFn,
getKey,
startSync: true,
staleTime: 60000, // uses the prefetched value without a refetch
})
)
expect(collection.status).toBe(`ready`)
expect(collection.size).toBe(2)
expect(Array.from(collection.values())).toEqual(
expect.arrayContaining(initialItems)
)
})
})
Loading