From 2c0284d4b28a903525b59d1ea7fe3e12950b8aaa Mon Sep 17 00:00:00 2001 From: Lalit Kapoor Date: Tue, 5 May 2026 00:35:23 -0400 Subject: [PATCH 1/2] fix(react-db): refresh live query snapshot after subscribe Force one post-subscribe snapshot refresh even when a collection is still loading so rows hydrated between render and subscription attachment are visible immediately. Add a regression test for the loading collection race and include a react-db patch changeset. --- .changeset/bright-beds-reflect.md | 5 ++ packages/react-db/src/useLiveQuery.ts | 12 +++-- packages/react-db/tests/useLiveQuery.test.tsx | 51 +++++++++++++++++++ 3 files changed, 63 insertions(+), 5 deletions(-) create mode 100644 .changeset/bright-beds-reflect.md diff --git a/.changeset/bright-beds-reflect.md b/.changeset/bright-beds-reflect.md new file mode 100644 index 0000000000..f4da834616 --- /dev/null +++ b/.changeset/bright-beds-reflect.md @@ -0,0 +1,5 @@ +--- +'@tanstack/react-db': patch +--- + +Refresh live query snapshots immediately after subscribing, even when the collection is still loading. diff --git a/packages/react-db/src/useLiveQuery.ts b/packages/react-db/src/useLiveQuery.ts index 331ff3a279..663a13caec 100644 --- a/packages/react-db/src/useLiveQuery.ts +++ b/packages/react-db/src/useLiveQuery.ts @@ -439,11 +439,13 @@ export function useLiveQuery( versionRef.current += 1 onStoreChange() }) - // Collection may be ready and will not receive initial `subscribeChanges()` - if (collectionRef.current.status === `ready`) { - versionRef.current += 1 - onStoreChange() - } + // The collection may have changed between render-time getSnapshot() and + // this subscription being attached. This is common when an on-demand query + // hydrates local rows immediately but keeps the collection loading while + // remote sync finishes. Force one post-subscribe snapshot refresh + // regardless of status so React sees those rows. + versionRef.current += 1 + onStoreChange() return () => { subscription.unsubscribe() } diff --git a/packages/react-db/tests/useLiveQuery.test.tsx b/packages/react-db/tests/useLiveQuery.test.tsx index fbb48d882c..61651199a2 100644 --- a/packages/react-db/tests/useLiveQuery.test.tsx +++ b/packages/react-db/tests/useLiveQuery.test.tsx @@ -18,6 +18,7 @@ import { mockSyncCollectionOptions, stripVirtualProps, } from '../../db/tests/utils' +import type { SyncConfig } from '@tanstack/db' type Person = { id: string @@ -1559,6 +1560,56 @@ describe(`Query Collections`, () => { }) describe(`eager execution during sync`, () => { + it(`refreshes the snapshot after subscribing while the collection is still loading`, async () => { + type TestRow = { id: string; value: number } + + let syncBegin: (() => void) | undefined + let syncWrite: Parameters[`sync`]>[0][`write`] + let syncCommit: (() => void) | undefined + let didWriteAfterSnapshot = false + + const collection = createCollection({ + id: `loading-subscribe-snapshot-refresh`, + getKey: row => row.id, + startSync: false, + sync: { + sync: ({ begin, write, commit }) => { + syncBegin = begin + syncWrite = write + syncCommit = commit + }, + }, + }) + + const { result } = renderHook(() => { + const queryResult = useLiveQuery(collection) + + if (!didWriteAfterSnapshot) { + didWriteAfterSnapshot = true + // Simulate a collection receiving rows after useLiveQuery read its + // render-time snapshot, but before React attached the external-store + // subscription. The collection intentionally stays loading because + // the missed update should still be visible before remote sync + // finishes. + syncBegin!() + syncWrite({ + type: `insert`, + value: { id: `1`, value: 1 }, + }) + syncCommit!() + } + + return queryResult + }) + + await waitFor(() => { + expect(result.current.data.map(row => stripVirtualProps(row))).toEqual([ + { id: `1`, value: 1 }, + ]) + }) + expect(result.current.isLoading).toBe(true) + }) + it(`should show state while isLoading is true during sync`, async () => { let syncBegin: (() => void) | undefined let syncWrite: ((op: any) => void) | undefined From 173cf0046e55f5391ce5458dfddb6463cb1a5f0c Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 5 May 2026 08:04:22 +0000 Subject: [PATCH 2/2] ci: apply automated fixes --- packages/react-db/tests/useLiveQuery.test.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/react-db/tests/useLiveQuery.test.tsx b/packages/react-db/tests/useLiveQuery.test.tsx index 61651199a2..4ad2c25093 100644 --- a/packages/react-db/tests/useLiveQuery.test.tsx +++ b/packages/react-db/tests/useLiveQuery.test.tsx @@ -1570,7 +1570,7 @@ describe(`Query Collections`, () => { const collection = createCollection({ id: `loading-subscribe-snapshot-refresh`, - getKey: row => row.id, + getKey: (row) => row.id, startSync: false, sync: { sync: ({ begin, write, commit }) => { @@ -1603,9 +1603,9 @@ describe(`Query Collections`, () => { }) await waitFor(() => { - expect(result.current.data.map(row => stripVirtualProps(row))).toEqual([ - { id: `1`, value: 1 }, - ]) + expect( + result.current.data.map((row) => stripVirtualProps(row)), + ).toEqual([{ id: `1`, value: 1 }]) }) expect(result.current.isLoading).toBe(true) })