TanStack Query 乐观更新模式:即时 UI 反馈与回滚保护

在 TanStack Query 中实现乐观更新:即时 UI 反馈 + 回滚保护,覆盖 mutation 模式、缓存操作与错误恢复,附完整 TypeScript 示例。

Sam Rivera
Sam Rivera
June 1, 20266 min read
其他语言:English · Deutsch

TL;DR: 乐观更新让 SaaS 应用「感觉瞬间响应」——在服务器响应之前就更新 UI。TanStack Query 的 onMutate 回调给了你一个干净的模式:快照当前缓存、应用乐观变更、出错时回滚。本指南覆盖三种模式——单条更新、列表变更和分页列表,附完整 TypeScript 示例。


引言

用户把速度当成产品特性。一次 300 毫秒的服务器调用会显得迟钝。乐观更新消除这种迟钝感:立即显示结果,在后台与服务器对账。

TanStack Query 通过 useMutation hook 的生命周期回调,为这个模式提供了一流支持。


基础乐观更新

tsx
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { updateProductFn } from '../server/products'

export function useOptimisticUpdate() {
  const queryClient = useQueryClient()

  return useMutation({
    mutationFn: updateProductFn,
    onMutate: async (newProduct) => {
      await queryClient.cancelQueries({ queryKey: ['product', newProduct.id] })
      const previous = queryClient.getQueryData(['product', newProduct.id])
      queryClient.setQueryData(['product', newProduct.id], newProduct)
      return { previous }
    },
    onError: (err, newProduct, context) => {
      queryClient.setQueryData(['product', newProduct.id], context?.previous)
    },
    onSettled: () => {
      queryClient.invalidateQueries({ queryKey: ['products'] })
    },
  })
}

列表变更的乐观更新

对列表查询,你需要操作缓存中的数组:

tsx
useMutation({
  mutationFn: createProductFn,
  onMutate: async (newProduct) => {
    await queryClient.cancelQueries({ queryKey: ['products'] })
    const previous = queryClient.getQueryData(['products'])

    queryClient.setQueryData(['products'], (old: any) => ({
      pages: old.pages.map((page: any, i: number) =>
        i === 0 ? { ...page, items: [newProduct, ...page.items] } : page
      ),
      pageParams: old.pageParams,
    }))

    return { previous }
  },
  onError: (err, _, context) => {
    queryClient.setQueryData(['products'], context?.previous)
  },
})

回滚决策矩阵

失败类型UX 响应技术动作
网络错误回滚 + toastonError 回滚
校验错误显示字段错误保留乐观状态 + 标记无效
授权错误回滚 + 重定向onError + 路由重定向
冲突(陈旧数据)回滚 + 重新拉取onSettled 失效处理

结论

乐观更新能大幅改善体感性能。TanStack Query 的 mutation 回调提供了一个干净、可测试的模式,无需手动状态管理的复杂度就能实现。

把乐观更新与正确的缓存失效策略配对,构成完整的数据同步方案。server functions 与 TanStack Query 的集成方式见Server Functions 最佳实践。