Skip to content

Commit 80fdac7

Browse files
authored
Add onInsert/onUpdate/onDelete handlers to collections (#156)
1 parent b847595 commit 80fdac7

File tree

13 files changed

+1145
-384
lines changed

13 files changed

+1145
-384
lines changed

.changeset/light-hands-love.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
"@tanstack/db-collections": patch
3+
"@tanstack/db-example-react-todo": patch
4+
"@tanstack/db": patch
5+
---
6+
7+
This change introduces a more streamlined and intuitive API for handling mutations by allowing `onInsert`, `onUpdate`, and `onDelete` handlers to be defined directly on the collection configuration.
8+
9+
When `collection.insert()`, `.update()`, or `.delete()` are called outside of an explicit transaction (i.e., not within `useOptimisticMutation`), the library now automatically creates a single-operation transaction and invokes the corresponding handler to persist the change.
10+
11+
Key changes:
12+
13+
- **`@tanstack/db`**: The `Collection` class now supports `onInsert`, `onUpdate`, and `onDelete` in its configuration. Direct calls to mutation methods will throw an error if the corresponding handler is not defined.
14+
- **`@tanstack/db-collections`**:
15+
- `queryCollectionOptions` now accepts the new handlers and will automatically `refetch` the collection's query after a handler successfully completes. This behavior can be disabled if the handler returns `{ refetch: false }`.
16+
- `electricCollectionOptions` also accepts the new handlers. These handlers are now required to return an object with a transaction ID (`{ txid: string }`). The collection then automatically waits for this `txid` to be synced back before resolving the mutation, ensuring consistency.
17+
- **Breaking Change**: Calling `collection.insert()`, `.update()`, or `.delete()` without being inside a `useOptimisticMutation` callback and without a corresponding persistence handler (`onInsert`, etc.) configured on the collection will now throw an error.
18+
19+
This new pattern simplifies the most common use cases, making the code more declarative. The `useOptimisticMutation` hook remains available for more complex scenarios, such as transactions involving multiple mutations across different collections.
20+
21+
---
22+
23+
The documentation and the React Todo example application have been significantly refactored to adopt the new direct persistence handler pattern as the primary way to perform mutations.
24+
25+
- The `README.md` and `docs/overview.md` files have been updated to de-emphasize `useOptimisticMutation` for simple writes. They now showcase the much simpler API of calling `collection.insert()` directly and defining persistence logic in the collection's configuration.
26+
- The React Todo example (`examples/react/todo/src/App.tsx`) has been completely overhauled. All instances of `useOptimisticMutation` have been removed and replaced with the new `onInsert`, `onUpdate`, and `onDelete` handlers, resulting in cleaner and more concise code.

.gitignore

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,3 +129,19 @@ dist
129129
.yarn/install-state.gz
130130
.pnp.*
131131
.DS_Store
132+
133+
# Added by Task Master AI
134+
dev-debug.log
135+
# Environment variables
136+
# Editor directories and files
137+
.idea
138+
.vscode
139+
*.suo
140+
*.ntvs*
141+
*.njsproj
142+
*.sln
143+
*.sw?
144+
# OS specific
145+
# Task files
146+
tasks.json
147+
tasks/

README.md

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -72,31 +72,29 @@ const Todos = () => {
7272
}
7373
```
7474

75-
Apply transactional writes with local optimistic state:
75+
Apply mutations with local optimistic state:
7676

7777
```tsx
78-
import { useOptimisticMutation } from "@tanstack/react-db"
78+
// Define collection with persistence handlers
79+
const todoCollection = createCollection({
80+
id: "todos",
81+
// ... other config
82+
onInsert: async ({ transaction }) => {
83+
const modified = transaction.mutations[0].modified
84+
await api.todos.create(modified)
85+
},
86+
})
7987

88+
// Then use collection operators in your components
8089
const AddTodo = () => {
81-
const addTodo = useOptimisticMutation({
82-
mutationFn: async ({ transaction }) => {
83-
const { collection, modified: newTodo } = transaction.mutations[0]!
84-
85-
await api.todos.create(newTodo)
86-
await collection.refetch()
87-
},
88-
})
89-
9090
return (
9191
<Button
9292
onClick={() =>
93-
addTodo.mutate(() =>
94-
todoCollection.insert({
95-
id: uuid(),
96-
text: "🔥 Make app faster",
97-
completed: false,
98-
})
99-
)
93+
todoCollection.insert({
94+
id: uuid(),
95+
text: "🔥 Make app faster",
96+
completed: false,
97+
})
10098
}
10199
/>
102100
)

0 commit comments

Comments
 (0)