Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
39 changes: 39 additions & 0 deletions .changeset/fix-writeinsert-optimistic-state.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
"@tanstack/db": patch
---

Fix optimistic state not being replaced by server data when using writeInsert in mutation handlers

Previously, when using `writeInsert()` inside an `onInsert` handler to sync server-generated data back to the collection, the optimistic client-side data would not be replaced by the actual server data. This meant that temporary client-side values (like negative IDs) would persist even after the server returned the real values.

**Example of the issue:**

```ts
const todosCollection = createCollection(
queryCollectionOptions({
// ...
onInsert: async ({ transaction }) => {
const newItems = transaction.mutations.map((m) => m.modified)
const serverItems = await createTodos(newItems)

todosCollection.utils.writeBatch(() => {
serverItems.forEach((serverItem) => {
todosCollection.utils.writeInsert(serverItem)
})
})

return { refetch: false }
},
})
)

// User inserts with temporary ID
todosCollection.insert({
id: -1234, // Temporary negative ID
title: "Task",
})

// Server returns real ID, but UI would still show -1234 instead of the real ID
```

This has been fixed by preventing optimistic state from `persisting` transactions from being re-applied during server data synchronization. The UI now correctly updates to show server-generated values once they are synced via `writeInsert()`.
11 changes: 11 additions & 0 deletions packages/db/src/collection/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -674,6 +674,17 @@ export class CollectionStateManager<
this.isThisCollection(mutation.collection) &&
mutation.optimistic
) {
// Skip re-applying optimistic state for persisting transactions
// if the mutation key was just synced (prevents overwriting fresh server data)
// EXCEPT during truncate operations where optimistic state should always be preserved
if (
!hasTruncateSync &&
transaction.state === `persisting` &&
changedKeys.has(mutation.key)
) {
continue
}

switch (mutation.type) {
case `insert`:
case `update`:
Expand Down
Loading