diff --git a/packages/query-db-collection/src/query.ts b/packages/query-db-collection/src/query.ts index 1a3074fc9b..021471f7d6 100644 --- a/packages/query-db-collection/src/query.ts +++ b/packages/query-db-collection/src/query.ts @@ -1,4 +1,4 @@ -import { QueryObserver } from "@tanstack/query-core" +import { QueryObserver, partialMatchKey } from "@tanstack/query-core" import { GetKeyRequiredError, QueryClientRequiredError, @@ -488,8 +488,25 @@ export function queryCollectionOptions< } }) + const invalidationUnsubscribe = queryClient + .getQueryCache() + .subscribe((event) => { + const { type, action, query } = event + + if ( + type !== `updated` || + action.type !== `invalidate` || + !partialMatchKey(queryKey, query.queryKey) + ) { + return + } + + refetch() + }) + return async () => { actualUnsubscribeFn() + invalidationUnsubscribe() await queryClient.cancelQueries({ queryKey }) queryClient.removeQueries({ queryKey }) } diff --git a/packages/query-db-collection/tests/query.test.ts b/packages/query-db-collection/tests/query.test.ts index 4c45bc57c9..2d5b6eac72 100644 --- a/packages/query-db-collection/tests/query.test.ts +++ b/packages/query-db-collection/tests/query.test.ts @@ -834,7 +834,7 @@ describe(`QueryCollection`, () => { // Wait for refetch to complete await vi.waitFor(() => { - expect(queryFn).toHaveBeenCalledTimes(2) + expect(queryFn).toHaveBeenCalledTimes(3) expect(collection.size).toBe(2) }) @@ -1603,4 +1603,36 @@ describe(`QueryCollection`, () => { expect(collection.size).toBe(0) expect(collection.status).toBe(`ready`) }) + + it(`should not refetch after cleanup unsubscribes from cache`, async () => { + const queryKey = [`todos`, 123] + const queryFn = vi.fn().mockResolvedValue([{ id: `1`, name: `Initial` }]) + + const config: QueryCollectionConfig = { + id: `unsubscribe-test`, + queryClient, + queryKey, + queryFn, + getKey, + startSync: true, + } + + const collection = createCollection(queryCollectionOptions(config)) + + await vi.waitFor(() => { + expect(queryFn).toHaveBeenCalledTimes(1) + }) + + await collection.cleanup() // Should unsubscribe from QueryCache + + // Update data + queryFn.mockResolvedValue([{ id: `1`, name: `Updated` }]) + + // This should NOT trigger another fetch + await queryClient.invalidateQueries({ queryKey: [`todos`] }) + + await new Promise((r) => setTimeout(r, 100)) // Give event loop time + + expect(queryFn).toHaveBeenCalledTimes(1) + }) })