Next.js+TypeScript中使用SWR Hook更新缓存如何避免重拉取新增帖子?
解决方案
问题根源
你当前遇到的问题来源于评论/点赞操作完成后,调用了无参数的mutate方法:SWR在只传入缓存key的情况下,会默认触发该key对应接口的全量重请求,拉取到其他用户发布的新帖后就会覆盖本地缓存,导致原有帖子排序变化。
代码修改方案
1. 优化评论提交逻辑
你已经做了本地缓存的乐观更新,只需要移除后续触发全量重拉的mutate调用即可;如果需要对齐服务端返回的最新数据,可让createComment接口返回更新后的单条帖子数据,再用该数据更新缓存即可,全程不需要触发全量接口请求。
修改后的handleSubmit代码如下:
// Create comment const handleSubmit = async (e: FormEvent<HTMLFormElement>): Promise<void> => { e.preventDefault(); // 先缓存原有数据,用于出错回滚 const originalPubs = mutate(`${process.env.URL}/api/publication`, (allPubs: Ipublication[]) => { // 这里优化原查找逻辑,用_id匹配代替引用匹配,避免找不到对应帖子 const currentPub = allPubs.find(f => f._id === publication._id); const updatePub = allPubs.map(pub => pub._id === currentPub._id ? { ...currentPub, comments: [ { body: commentBody, createdAt: new Date().toISOString(), identifier: userAuth.user.id, name: userAuth.user.name }, ...currentPub.comments ] } : pub ); return updatePub; }, false); // 第三个参数传false,关闭重验证 try { // 可让createComment接口返回更新后的当前帖子完整数据 const updatedPub = await createComment( { identifier: userAuth.user.id, body: commentBody }, publication._id ); // 可选:用服务端返回的最新数据更新缓存,依旧关闭重验证 mutate(`${process.env.URL}/api/publication`, (allPubs: Ipublication[]) => { return allPubs.map(pub => pub._id === updatedPub._id ? updatedPub : pub); }, false); } catch (err) { // 出错时回滚到原有缓存数据,不要触发全量拉取 mutate(`${process.env.URL}/api/publication`, originalPubs, false); } };
2. 点赞逻辑参考修改
和评论逻辑一致,先做乐观更新,接口请求完成后用返回数据对齐缓存,全程不触发全量重拉即可。
3. 用户自主发帖新增逻辑
当用户自己发布新帖时,手动将新帖子插入缓存列表头部即可,实现示例:
const handleCreatePub = async (newPubData) => { try { // 提交新帖到服务端,接口返回新帖完整数据 const createdPub = await createNewPub(newPubData); // 将新帖插入缓存列表头部,关闭重验证 mutate(`${process.env.URL}/api/publication`, (allPubs: Ipublication[]) => { return [createdPub, ...allPubs]; }, false); } catch (err) { // 自定义错误处理逻辑 } }
额外优化建议
如果需要完全避免页面后续任何场景的全量帖子拉取,可在useSWR的配置项中新增revalidateOnMount: false,关闭组件挂载时的自动重验证:
const { data: Publications }: thePublication = useSWR( `${process.env.URL}/api/publication`, { initialData: allPubs, revalidateOnFocus: false, revalidateOnMount: false // 新增该配置 } );
内容的提问来源于stack exchange,提问作者Diego Davincci
相关产品推荐
相关产品推荐

