应该手动更新缓存始终是首选的选择突变后,只要我得到适当的数据从服务器?



我正在用React Query编写一个CRUD应用程序,我创建了一些自定义钩子,如下所述:https://react-query.tanstack.com/examples/custom-hooks

在文档中,我看到基本上有两种方法在突变后更新缓存:

  • 查询无效(https://react-query.tanstack.com/guides/query-invalidation)
onSuccess: () => {
queryClient.invalidateQueries("posts");
}
  • 手动更新缓存(https://react-query.tanstack.com/guides/invalidations-from-mutations)
// Update post example
// I get the updated post data for onSuccess
onSuccess: (data) => {
queryClient.setQueryData("posts", (oldData) => {
const index = oldData.findIndex((post) => post.id === data.id);
if (index > -1) {
return [
...oldData.slice(0, index),
data,
...oldData.slice(index + 1),
];
}
});
},

我知道手动更新的优点是不需要额外的调用来获取"帖子",但是我想知道是否有任何无效缓存优于手动更新的优势。例如:

import { useMutation, useQueryClient } from "react-query";
const { API_URL } = process.env;
const createPost = async (payload) => {
const options = {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
};
if (API_URL) {
try {
const response = await fetch(API_URL, options);
if (!response.ok) {
throw new Error(response.statusText);
}
return response.json();
} catch (error) {
throw new Error(error);
}
} else {
throw new Error("No api url is set");
}
};
export default function useCreatePost() {
const queryClient = useQueryClient();
return useMutation((payload) => createPost(payload), {
// DOES INVALIDATING HAVE ANY ADVANTAGE OVER MANUAL UPDATE IN THIS CASE?
// onSuccess: () => {
//   queryClient.invalidateQueries("posts");
// },
onSuccess: (data) => {
queryClient.setQueryData("posts", (oldData) => {
return [...oldData, data];
});
},
});
}

谢谢你的时间!

正如您自己所说,唯一的好处是您不会浪费另一个网络调用来更新我们已经拥有的数据.

这里有一个创建和删除的例子。

import { useMutation, useQueryClient } from 'react-query'

const queryClient = useQueryClient()
// createPost(post: PostT) {
//   const { data } = await http.post<{ post: PostT >('/posts', { post });
//   return data.post;
// }
const mutation = useMutation(createPost, {
onSuccess: (post) => {
queryClient.setQueryData<PostT[]>(['posts'], (oldData || []) => [ ...oldData, post])
},
})
// deletePost(id: string) {
//   await http.delete(`/posts/${id}`);
// }
const mutation = useMutation(deletePost, {
onSuccess: (_, id) => {
queryClient.setQueryData<PostT[]>(['posts'], (oldData || []) => oldData.filter((post) => id !== post.id)
},
})

在某些情况下,使查询无效也是一种选择。查询将无效,数据将被标记为过期。这将在后台触发重新抓取。所以你知道数据会尽可能的新鲜。

这很方便,如果你有:

  • 使用来自突变的数据更新多个查询
  • 有一个(困难的)嵌套数据结构来更新
import { useMutation, useQueryClient } from 'react-query'

const queryClient = useQueryClient()
const mutation = useMutation(createPost, {
onSuccess: () => {
queryClient.invalidateQueries('posts')
queryClient.invalidateQueries('meta')
queryClient.invalidateQueries('headers')
},
})

但这真的取决于你。

使用手动更新的主要优点在于,您可以在请求发送到服务器之前进行手动更新;因此,如果您在请求成功后手动更新,那么如果您从服务器获得的数据不需要立即呈现给用户,那么就没有多少优势了。在这些情况下(我发现这是大多数情况),你最好放弃。当您使用乐观更新时,在将请求发送到服务器之前,您假设请求成功。如果请求失败,你就回滚你的更新。这样你的动作就会立即发生,这是一个比显示加载旋转器之类的动作更好的用户体验。然后显示更新后的状态。所以我发现它给用户即时反馈比保存一个额外的请求到服务器更有用。在大多数情况下(如您的情况),您仍然需要在之后使查询无效,因为您手动添加的帖子没有id,因此您应该将其与来自服务器的帖子列表同步。所以要非常小心因为如果你从页面的其他地方读取id,它就会是undefined &会抛出一个错误。所以在一天结束的时候你的突变并不是乐观更新的一个很好的候选者;你应该小心处理所有的问题,可以提出你的帖子值有一个帖子没有id在它(而不是像一个跟随行动,这只是改变一个布尔值在你的数据库&您可以自信地改变缓存&如果请求不成功,撤销它)。如果我们假设你可以处理这个问题你的useMutation钩子应该是这样的:

return useMutation(
(payload) => {
queryClient.setQueryData("posts", (oldData) => {
return [...oldData, payload];
});
return createPost(payload);
},
{
onSettled: () => {
queryClient.invalidateQueries("posts");
},
}
);

最新更新