使用 React-Query (TRPC) 进行乐观更新

ant*_*elm 3 javascript reactjs react-query trpc.io

我不确定如何使用 trpc 进行乐观更新?这是“内置的”还是我必须使用react-query的useQuery钩子?

到目前为止,我正在尝试这样做,但它不起作用:

 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 }
        },
//...
Run Code Online (Sandbox Code Playgroud)

这看起来应该有意义吗?它正在更新价值,但并不乐观。

小智 12

经过两天的搜索,我终于找到了解决这个问题的方法。

我们必须像前面提到的答案一样使用 api.useContext() 。

这是正在运行的完整乐观更新示例。

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>
  );
};
Run Code Online (Sandbox Code Playgroud)


TkD*_*odo 9

queryClienttrpc v10通过自己的钩子提供大多数函数的类型安全变体useContext

const utils = trpc.useContext()
Run Code Online (Sandbox Code Playgroud)

那么,你应该能够做到:

utils.text.getOne.cancel()
utils.text.getOne.getData()
utils.text.getOne.setData()
Run Code Online (Sandbox Code Playgroud)

请参阅: https: //trpc.io/docs/useContext

  • 这是乐观更新的关键 - 您必须在前端复制通常发生在后端的逻辑。这是我尝试避免它们的原因之一:https://tkdodo.eu/blog/mastering-mutations-in-react-query#optimistic-updates (3认同)