MapleLeaf 🍁
MapleLeaf 🍁11mo ago

optimistic update syntax

imperative optimistic updates have been tedious and brittle compared to declarative in my experience, is all AFAICT the declarative example only grows by a handful of lines to account for multiple mutations if I wanted that
3 Replies
sshader
sshader11mo ago
I tried to write up the version of this using withOptimisticUpdate and was curious which parts felt brittle and tedious here:
const createRectangle = useMutation(api.rectangles.create).withOptimisticUpdate((localQueryStore, args) => {
const newReactangle = {
...args,
_id: "new" as Id<"rectangles">,
_creationTime: Date.now(),
}
// I'm guessing there's some link between the args to create a rectangle and the room
// that's not included in the code snippet
const listQueries = localQueryStore.getAllQueries(api.rectangles.list).filter(q => q.args.roomId === args.roomId);
for (const listQuery of listQueries) {
localQueryStore.setQuery(api.rectangles.list, listQuery.args, [...listQuery.value, newReactangle])
}
})

const updateRectangle = useMutation(api.rectangles.update).withOptimisticUpdate((localQueryStore, args) => {
const listQueries = localQueryStore.getAllQueries(api.rectangles.list)
for (const listQuery of listQueries) {
localQueryStore.setQuery(api.rectangles.list, listQuery.args, listQuery.value.map(it => it._id === args.id ? {...it, args } : it))
}
})
const deleteRectangle = useMutation(api.rectangles.remove).withOptimisticUpdate((localQueryStore, args) => {
const listQueries = localQueryStore.getAllQueries(api.rectangles.list)
for (const listQuery of listQueries) {
localQueryStore.setQuery(api.rectangles.list, listQuery.args, listQuery.value.filter(it => it._id !== args.id))
}
})
const createRectangle = useMutation(api.rectangles.create).withOptimisticUpdate((localQueryStore, args) => {
const newReactangle = {
...args,
_id: "new" as Id<"rectangles">,
_creationTime: Date.now(),
}
// I'm guessing there's some link between the args to create a rectangle and the room
// that's not included in the code snippet
const listQueries = localQueryStore.getAllQueries(api.rectangles.list).filter(q => q.args.roomId === args.roomId);
for (const listQuery of listQueries) {
localQueryStore.setQuery(api.rectangles.list, listQuery.args, [...listQuery.value, newReactangle])
}
})

const updateRectangle = useMutation(api.rectangles.update).withOptimisticUpdate((localQueryStore, args) => {
const listQueries = localQueryStore.getAllQueries(api.rectangles.list)
for (const listQuery of listQueries) {
localQueryStore.setQuery(api.rectangles.list, listQuery.args, listQuery.value.map(it => it._id === args.id ? {...it, args } : it))
}
})
const deleteRectangle = useMutation(api.rectangles.remove).withOptimisticUpdate((localQueryStore, args) => {
const listQueries = localQueryStore.getAllQueries(api.rectangles.list)
for (const listQuery of listQueries) {
localQueryStore.setQuery(api.rectangles.list, listQuery.args, listQuery.value.filter(it => it._id !== args.id))
}
})
In my mind, this mostly feels like a difference of whether you want to write your logic near each mutation vs. near each query (like the code snippet you shared earlier). (one of the benefits for writing the logic for each mutation instead of for each query comes into play if you have the same query loaded in multiple places like useQuery(api.users.currentUser) and you want to update all of them so they're all consistent with each other)
MapleLeaf 🍁
MapleLeaf 🍁OP11mo ago
I mean, if I had to say, the declarative version just maps in my brain better :akkoshrug: writing it in terms of the current query data + mutations in flight I will say that Convex's current optimistic update API does a better job than others I've worked with (React Query)
sshader
sshader11mo ago
Yeah fair that one framing can make more sense vs. the other (almost based on personal preference). I think we're basically talking about the difference between "when I call the create mutation, I add something to all the list queries" vs "when I load the list query, I add something if I have any pending create mutations" (IMO these seem equally declarative / imperative but are just different framings ¯\_(ツ)_/¯)

Did you find this page helpful?