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

TRPC+Prisma+NextJS Todo应用:如何简化单个Todo的乐观更新?

简化TRPC Todo应用的乐观更新逻辑

问题背景

你当前通过深拷贝整个Todo列表再遍历修改的方式实现乐观更新,希望简化逻辑,像React Query那样直接定位单个Todo项修改缓存,避免全量更新。

解决方案

方案1:直接用TRPC底层的React Query queryClient操作缓存

TRPC基于React Query实现,所以可以直接获取queryClient,用函数式更新修改列表中的单个项,无需全量拷贝:

import { useQueryClient } from '@tanstack/react-query';
import { api } from '../utils/trpc'; // 替换成你的TRPC客户端路径

const useToggleTodo = () => {
  const queryClient = useQueryClient();
  const toggleMutation = api.todo.toggle.useMutation({
    // 乐观更新触发时
    onMutate: async ({ id, completed }) => {
      // 取消正在进行的Todo列表查询,防止覆盖乐观更新
      await queryClient.cancelQueries({ queryKey: ['todo.getAll'] });
      
      // 缓存当前Todo列表的快照,用于失败回滚
      const previousTodos = queryClient.getQueryData<Todo[]>(['todo.getAll']);

      // 直接修改缓存中对应ID的Todo状态
      queryClient.setQueryData(['todo.getAll'], (oldTodos) => {
        if (!oldTodos) return [];
        return oldTodos.map(todo => 
          todo.id === id ? { ...todo, completed } : todo
        );
      });

      // 返回上下文,供错误处理时回滚
      return { previousTodos };
    },

    // 当mutation失败时,回滚缓存
    onError: (_err, _vars, context) => {
      if (context?.previousTodos) {
        queryClient.setQueryData(['todo.getAll'], context.previousTodos);
      }
    },

    // 无论成功失败,最终刷新缓存确保和后端一致(可选)
    onSettled: () => {
      queryClient.invalidateQueries({ queryKey: ['todo.getAll'] });
    },
  });

  return toggleMutation;
};

方案2:优化你现有的TRPC ctx操作逻辑

如果坚持用TRPC的ctx方法,也可以去掉低效的JSON深拷贝,用对象展开运算符直接生成新数组:

// 获取当前缓存的Todo列表
const prevData = ctx.todos.getAll.getData();
if (!prevData) return;

// 直接生成更新后的数组,无需深拷贝整个列表
const updatedTodos = prevData.map(todo => 
  todo.id === targetId ? { ...todo, completed: newCompletedState } : todo
);

// 更新缓存
ctx.todos.getAll.setData(undefined, updatedTodos);

关键注意事项

  • QueryKey必须一致:确保你操作的queryKey和获取Todo列表时的完全匹配(TRPC自动生成的queryKey格式为[路由名.方法名],比如['todo.getAll'])。
  • 避免JSON深拷贝:JSON序列化的深拷贝性能差且会丢失特殊类型,用对象展开或结构化克隆更安全高效。
  • 必须处理回滚:mutation失败时一定要恢复缓存到之前的状态,避免数据不一致。

内容的提问来源于stack exchange,提问作者José Pino

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 15:22:49