乐观更新 Optimistic Updates
在执行修改之前,当你"乐观"地打算进行更新时,修改有可能失败(可能性非零)。 在大多数失败的情况下,你可以轻松地为查询触发重新获取,使其恢复到真正的和服务器一致的状态。 但是在某些情况下,重新获取可能没法正确的工作,修改可能代表某种服务器端的问题,从而无法进行。 在这种情况下,你可以选择回滚更新。
为此,useMutation 的 onMutate 回调允许返回一个特定值,该值稍后将作为最后一个参数传递给 onError 和 onSettled 处理--在大多数情况下,以这种方式来传递一个回滚函数是最有用的。
添加新的 todo 时更新 todos 列表
TypeScript
1const queryClient = useQueryClient();2 3useMutation({4 mutationFn: updateTodo,5 // 当 mutate 调用时6 onMutate: async (newTodo) => {7 // 撤销相关的查询(这样它们就不会覆盖我们的乐观更新)8 await queryClient.cancelQueries(["todos"]);9 10 // 保存前一次状态的快照11 const previousTodos = queryClient.getQueryData(["todos"]);12 13 // 执行"乐观"更新14 queryClient.setQueryData(["todos"], (old) => [...old, newTodo]);15 16 // 返回具有快照值的上下文对象17 return { previousTodos };18 },19 // 如果修改失败,则使用 onMutate 返回的上下文进行回滚20 onError: (err, newTodo, context) => {21 queryClient.setQueryData(["todos"], context.previousTodos);22 },23 // 总是在错误或成功之后重新获取:24 onSettled: () => {25 queryClient.invalidateQueries("todos");26 },27});更新单个的 todo
TypeScript
1useMutation({2 mutationFn: updateTodo,3 // 当 mutate 调用时4 onMutate: async (newTodo) => {5 // 取消相关的获取数据逻辑(这样它们就不会覆盖我们的乐观更新)6 await queryClient.cancelQueries(["todos", newTodo.id]);7 8 // 保存前一次状态的快照9 const previousTodo = queryClient.getQueryData(["todos", newTodo.id]);10 11 // 执行乐观更新12 queryClient.setQueryData(["todos", newTodo.id], newTodo);13 14 // 返回具有快照值和修改值的上下文对象15 return { previousTodo, newTodo };16 },17 // 如果修改失败,则使用 onMutate 返回的上下文18 onError: (err, newTodo, context) => {19 queryClient.setQueryData(20 ["todos", context.newTodo.id],21 context.previousTodo,22 );23 },24 // 总是在错误或成功之后重新获取:25 onSettled: (newTodo) => {26 queryClient.invalidateQueries(["todos", newTodo.id]);27 },28});如果你愿意,你也可以使用 onsettled 函数来代替 onError 和 onSuccess 做处理:
TypeScript
1useMutation({2 mutationFn: updateTodo,3 // ...4 onSettled: (newTodo, error, variables, context) => {5 if (error) {6 // do something7 }8 },9});