从变更响应中更新数据

当处理那些在服务器上更新对象的变更(mutation)时,通常新的对象会自动作为变更响应的一部分返回。我们无需重新获取该对象的查询,从而避免了浪费一次网络请求来获取我们已经拥有的数据。相反,我们可以利用变更函数返回的对象,立即使用 Query Client 的 setQueryData 方法,将现有查询更新为新数据:

const queryClient = useQueryClient()

const mutation = useMutation({
  mutationFn: editTodo,
  onSuccess: (data) => {
    queryClient.setQueryData(['todo', { id: 5 }], data)
  },
})

mutation.mutate({
  id: 5,
  name: '洗衣服',
})

// 以下查询将使用成功变更的响应数据进行更新
const { status, data, error } = useQuery({
  queryKey: ['todo', { id: 5 }],
  queryFn: fetchTodoById,
})

你可能希望将 onSuccess 逻辑封装到一个可复用的变更中,为此你可以创建一个像这样的自定义 Hook:

const useMutateTodo = () => {
  const queryClient = useQueryClient()

  return useMutation({
    mutationFn: editTodo,
    // 注意第二个参数是 `mutate` 函数接收的 variables 对象
    onSuccess: (data, variables) => {
      queryClient.setQueryData(['todo', { id: variables.id }], data)
    },
  })
}

不可变性(Immutability)

通过 setQueryData 进行的更新必须以不可变的方式执行。切勿尝试通过直接修改(从缓存中获取的)数据来原地写入缓存。虽然它可能一开始能工作,但最终会引发一些难以察觉的 bug。

queryClient.setQueryData(['posts', { id }], (oldData) => {
  if (oldData) {
    // ❌ 不要尝试这样做
    oldData.title = '我的新文章标题'
  }
  return oldData
})

queryClient.setQueryData(
  ['posts', { id }],
  // ✅ 这才是正确的方式
  (oldData) =>
    oldData
      ? {
          ...oldData,
          title: '我的新文章标题',
        }
      : oldData,
)

最后更新于

这有帮助吗?