-
Notifications
You must be signed in to change notification settings - Fork 198
Description
- I've validated the bug against the latest version of DB packages
Describe the bug
When using useLiveSuspenseQuery with a queryCollectionOptions collection in syncMode: "on-demand", changing the subset scope (here via userId) causes the Suspense boundary to remain stuck on the fallback indefinitely.
The backend request for the new subset is executed correctly, but the Suspense boundary never resolves.
✅ useLiveSuspenseQuery + eager
❌ useLiveSuspenseQuery + on-demand
✅ useLiveQuery + on-demand
To Reproduce
Steps to reproduce the behavior:
- Go to repro link
- Click on tab n°2:
useLiveSuspenseQuery (on-demand) - Wait for the first user to load
- Click
Next - Observe that the fallback is shown forever
Expected behavior
After clicking Next, user 2 should appear after the simulated 1 second delay.
Minimal implementation
Define an on-demand collection
export const usersOnDemandCollection = createCollection(
queryCollectionOptions({
syncMode: 'on-demand',
queryKey: ['users-on-demand'],
queryFn: async (ctx) => {
const options = parseLoadSubsetOptions(ctx.meta?.loadSubsetOptions)
const userId = options.filters.find((f) => f.field.join('.') === 'id')?.value as number
const user = await fetchUser(userId)
return [user]
},
queryClient,
getKey: (item) => item.id,
}),
)Define a Suspense live query for one user
export const useUserLiveSuspenseOnDemand = (userId: number) => {
const result = useLiveSuspenseQuery(
(q) => q.from({ users: usersOnDemandCollection }).where(({ users }) => eq(users.id, userId)).findOne(),
[userId],
)
return result
}Simple scenario where the userId comes from useState.
(Imagine it comes from the route params /users/:userId)
function Content() {
const [userId, setUserId] = useState<number>(1)
const result = useUserLiveSuspenseOnDemand(userId)
const user = result.data
return (
<div>
User: {user?.id} {user?.name}
<button type="button" onClick={() => setUserId((value) => Math.max(1, value - 1))} disabled={userId <= 1}>
Previous
</button>
<button type="button" onClick={() => setUserId((value) => Math.min(10, value + 1))} disabled={userId >= 10}>
Next
</button>
</div>
)
}
function Tab2StateDbSuspenseOnDemand() {
return (
<Suspense fallback={<div className="panel">Suspense fallback...</div>}>
<Content />
</Suspense>
)
}Additional context
Using a dynamic queryKey such as ['users-on-demand', userId] instead of ['users-on-demand'] does not change the behavior
The workaround discussed in #1343 neither
Environment
{
"@tanstack/db": "^0.6.0",
"@tanstack/query-db-collection": "^1.0.31",
"@tanstack/react-db": "^0.1.78",
"@tanstack/react-query": "^5.95.2",
"@tanstack/react-query-devtools": "^5.95.2",
"react": "^19.2.4",
"react-dom": "^19.2.4"
}