Reputation: 7553
I am not sure how I would do optimistic updates with trpc? Is this "built-in" or do I have to use react-query's useQuery hook?
So far, I am trying it like so, but it's not working:
const queryClient = useQueryClient();
const updateWord = trpc.word.update.useMutation({
onMutate: async newTodo => {
// Cancel any outgoing refetches (so they don't overwrite our optimistic update)
await queryClient.cancelQueries({ queryKey: ['text', 'getOne'] })
// Snapshot the previous value
const previousText = queryClient.getQueryData(['text', 'getOne'])
// Optimistically update to the new value
queryClient.setQueryData(['text', 'getOne'], old => old ? {...old, { title: "Hello" }} : undefined)
// Return a context object with the snapshotted value
return { previousText }
},
//...
Does this look like it should make sense? It's updating the value, but not optimistically.
Upvotes: 6
Views: 8316
Reputation: 359
After 2 days of searching how to do it, I finally found out how to solve this.
We have to use api.useContext() like previous answer mentioned.
Here is the complete optimistic update example that is working.
import { useCallback, useState } from "react";
import { Button } from "../shadcn-ui/button";
import { Input } from "../shadcn-ui/input";
import { api } from "~/utils/api";
import { useToast } from "~/components/shadcn-ui/use-toast";
import { type TodoWithUser } from "./Todo.type";
import { useSession } from "next-auth/react";
export const TodoBar = () => {
const [todo, setTodo] = useState("");
const { data: session } = useSession();
const utils = api.useContext();
const addTodo = api.todo.create.useMutation({
onMutate: async (newTodo) => {
setTodo("");
// Cancel any outgoing refetches
// (so they don't overwrite our optimistic update)
await utils.todo.findAll.cancel();
// Snapshot the previous value
const previousTodos = utils.todo.findAll.getData();
// Optimistically update to the new value
utils.todo.findAll.setData(
undefined,
(oldQueryData: TodoWithUser[] | undefined) =>
[
...(oldQueryData ?? []),
{
author: {
name: session?.user?.name,
id: session?.user?.id,
},
content: newTodo.content,
done: false,
createdAt: new Date(),
updatedAt: new Date(),
},
] as TodoWithUser[]
);
// Return a context object with the snapshotted value
return { previousTodos };
},
onError: (err, _newTodo, context) => {
// Rollback to the previous value if mutation fails
utils.todo.findAll.setData(undefined, context?.previousTodos);
},
onSuccess: () => {
console.log("inside onSuccess");
},
onSettled: () => {
void utils.todo.findAll.invalidate();
},
});
const handleInputOnChange = useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => {
setTodo(event.target.value);
},
[]
);
const handleAddTodo = useCallback(() => {
addTodo.mutate({
content: todo,
});
}, [addTodo, todo]);
return (
<div className="flex w-full items-center space-x-2 self-center px-6 pt-2 md:w-2/3 md:flex-grow-0 lg:w-2/3 xl:w-1/2">
<Input
placeholder="Enter your task!"
value={todo}
onChange={handleInputOnChange}
/>
<Button type="submit" onClick={handleAddTodo}>
Add
</Button>
</div>
);
};
Upvotes: 15
Reputation: 29046
trpc v10 offers type-safe variants of most functions from the queryClient
via their own useContext
hook:
const utils = trpc.useContext()
then, you should be able to do:
utils.text.getOne.cancel()
utils.text.getOne.getData()
utils.text.getOne.setData()
see: https://trpc.io/docs/useContext
Upvotes: 10