You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用tRPC与React-Query实现乐观更新时出现数据闪烁问题

问题:tRPC useMutation乐观更新出现数据闪烁

我用tRPC的useMutation结合React-Query实现乐观更新,功能整体正常,但遇到一个问题:更新数据时,新列表会快速显示出来,但几毫秒后又恢复为旧值,最终才重新显示新值。以下是我的实现代码:

import { api } from "@/utils/api";
import type { Category } from "@prisma/client";

export const useCategoryActions = () => {
  const utils = api.useContext();
  //Queries
  const list = api.category.list.useQuery();

  // Mutations
  const update = api.category.update.useMutation({
    onMutate: (variables) => {
      // Cancel any outgoing refetches (so they don't overwrite(race condition) our optimistic update)
      void utils.category.list.cancel();
      const previousQueryData = utils.category.list.getData();

      const newCategory: Category = {
        id: crypto.randomUUID(),
        name: variables.name,
        slug: variables.name,
        createdAt: new Date(),
        updatedAt: new Date(),
      };
      utils.category.list.setData(undefined, (oldQueryData) => {
        if (oldQueryData) {
          const filteredData =
            oldQueryData.filter((item) => item.slug !== variables.slug) ?? [];
          const elementIndex =
            oldQueryData.findIndex((item) => item.slug === variables.slug) ??
            -1;

          filteredData.splice(elementIndex, 0, newCategory);

          return filteredData;
        }
      });

      // return will pass the function or the value to the onError third argument:
      return () => utils.category.list.setData(undefined, previousQueryData);
    },
    onError: (error, variables, rollback) => {
      //   If there is an errror, then we will rollback
      if (rollback) {
        rollback();
        console.log("rollback");
      }
    },
    onSettled: async (data, variables, context) => {
      await utils.category.list.invalidate();
    },
  });

  return {
    list,
    update,
  };
};

我尝试了一个潜在解决方案,但似乎无效,可能是实现方式有误:

import { useRef } from "react";
import { api } from "@/utils/api";
import type { Category } from "@prisma/client";

export const useCategoryActions = () => {
  const utils = api.useContext();
  const mutationCounterRef = useRef(0);
  //Queries
  const list = api.category.list.useQuery();

  // Mutations
  const update = api.category.update.useMutation({
    onMutate: (variables) => {
      // Cancel any outgoing refetches (so they don't overwrite(race condition) our optimistic update)
      void utils.category.list.cancel();
      const previousQueryData = utils.category.list.getData();

      const newCategory: Category = {
        id: crypto.randomUUID(),
        name: variables.name,
        slug: variables.name,
        createdAt: new Date(),
        updatedAt: new Date(),
      };
      utils.category.list.setData(undefined, (oldQueryData) => {
        if (oldQueryData) {
          const filteredData =
            oldQueryData.filter((item) => item.slug !== variables.slug) ?? [];
          const elementIndex =
            oldQueryData.findIndex((item) => item.slug === variables.slug) ??
            -1;

          filteredData.splice(elementIndex, 0, newCategory);

          return filteredData;
        }
      });

      // Increment the mutation counter
      mutationCounterRef.current++;

      // return will pass the function or the value to the onError third argument:
      return async () => {
        // Decrement the mutation counter
        mutationCounterRef.current--;
        // Only invalidate queries if there are no ongoing mutations
        if (mutationCounterRef.current === 0) {
          utils.category.list.setData(undefined, previousQueryData);
          await utils.category.list.invalidate();
        }
      };
    },
    onError: async (error, variables, rollback) => {
      //   If there is an errror, then we will rollback
      if (rollback) {
        await rollback();
        console.log("rollback");
      }
    },
    onSettled: async (data, variables, context) => {
      // Decrement the mutation counter
      mutationCounterRef.current--;
      // Only invalidate queries if there are no ongoing mutations
      if (mutationCounterRef.current === 0) {
        await utils.category.list.invalidate();
      }
    },
  });

  return {
    list,
    update,
  };
};

内容的提问来源于stack exchange,提问作者John John

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 08:57:54