Skip to content
Open
Changes from all 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
43 changes: 43 additions & 0 deletions docs/guides/mutations.md
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,49 @@ todoCollection.update(todo.id, (draft) => {

If the handler throws an error during persistence, the optimistic state is automatically rolled back.

### Multiple Pending Transactions

When you make multiple mutations to the same item before earlier ones have synced back, each mutation creates a separate transaction. These transactions are **layered**, with the most recent transaction's state taking priority for display. Layering operates at the item level—the entire item is overlaid, not individual fields within it.

```tsx
// User rapidly updates a document's content: A → B → C
docCollection.update(docId, (draft) => { draft.content = "A" }) // tx1
docCollection.update(docId, (draft) => { draft.content = "B" }) // tx2
docCollection.update(docId, (draft) => { draft.content = "C" }) // tx3

// Visible state = "C" (latest transaction wins)
```

As each transaction syncs back from the server, the optimistic state is **recomputed** from the remaining pending transactions. This means your latest changes are preserved:

- When tx1 syncs back → tx2 and tx3 still pending → visible state remains "C"
- When tx2 syncs back → tx3 still pending → visible state remains "C"
- When tx3 syncs back → no pending transactions → visible state = synced server state

This layering ensures that rapid sequential updates feel instant and consistent, without earlier sync confirmations disrupting the user's most recent changes.

When you call `update()`, the draft reflects the **current visible state**, which includes optimistic changes from all pending transactions—not just your own. This means mutations build on top of each other:

```tsx
const tx1 = createTransaction({ mutationFn, autoCommit: false })
const tx2 = createTransaction({ mutationFn, autoCommit: false })

tx1.mutate(() => {
itemCollection.update("A", (draft) => { draft.value = 1 })
})

tx2.mutate(() => {
itemCollection.update("B", (draft) => { draft.value = 2 })
})

tx1.mutate(() => {
// draft.value here is 2 (from tx2), not the original synced value
itemCollection.update("B", (draft) => { draft.value = draft.value + 10 })
})

// Visible state: A=1, B=12
```

## Collection Write Operations

Collections support three core write operations: `insert`, `update`, and `delete`. Each operation applies optimistic state immediately and triggers the corresponding operation handler.
Expand Down
Loading