-
Notifications
You must be signed in to change notification settings - Fork 188
Description
Describe the bug
When multiple items in a queryCollectionOptions-backed collection are updated in rapid succession (e.g. toggling several task checkboxes quickly), each individual onUpdate call fires a server mutation and, upon resolution, triggers a refetch of the underlying queryFn. Because these refetches resolve at different times, a response that arrives after a later optimistic update can carry stale server data that overwrites the still-pending optimistic state — causing UI "flicker" where previously checked items revert to unchecked.
Not sure if this is expected behaviour or maybe there is a config that I'm missing.
To Reproduce
Collection setup (src/db-collections/index.ts):
export const tasksCollection = createCollection(
queryCollectionOptions({
queryKey: ['tasks'],
queryFn: async () => {
return await listTasks()
},
queryClient: getContext().queryClient,
getKey: (item) => item.id,
schema: taskSchemas.select,
onUpdate: async ({ transaction }) => {
await Promise.all(
transaction.mutations.map((mutation) =>
updateTask({
data: {
...mutation.changes,
id: mutation.original.id,
},
}),
),
)
},
}),
)
Component usage (src/routes/app/checklist.tsx):
// Live query
const tasks = useLiveSuspenseQuery((q) =>
q
.from({ tasks: tasksCollection })
.orderBy((t) => t.tasks.dueDate, 'desc'),
)
// Per-task toggle
function TaskRow({ task }) {
const done = task.status === 'done'
async function toggle() {
tasksCollection.update(task.id, (draft) => {
draft.status = done ? 'pending' : 'done'
})
}
// ...
}
Steps
- Render a list of tasks backed by queryCollectionOptions with an async onUpdate
- Rapidly click the checkbox on 3–5 different tasks within ~500ms of each other
- Observe that some tasks that were checked flicker back to unchecked before settling
Expected behavior
I would expect the collection to behave similarly to the pattern TanStack Query recommends for optimistic updates — where in-flight queries are cancelled when a mutation is fired to prevent them from overwriting optimistic state. In TanStack Query you have to do this manually via queryClient.cancelQueries() in onMutate. Since TanStack DB's queryCollectionOptions owns both the mutation lifecycle (onUpdate) and the underlying query (queryFn), I would expect it to automatically cancel or suppress any in-flight or pending refetches for the duration of a transaction — so that a stale server response can never clobber an optimistic update that is still pending.
Additional context
I'm not currently passing any explicit gcTime, staleTime, or mutation-debouncing options to queryCollectionOptions. If there is a config option (e.g. suppressing refetches while transactions are in-flight, a gcTime/staleTime setting, or a way to batch onUpdate calls) that would prevent completed refetches from clobbering pending optimistic state, I'd love to know what to set. I couldn't find guidance on this in the docs.