antonwilhelm
antonwilhelm

Reputation: 7553

Optimistic Updates with React-Query (TRPC)

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

Answers (2)

Baris Balli
Baris Balli

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

TkDodo
TkDodo

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

Related Questions