useSWRInfinite分页时mutate的optimisticData不即时生效问题
useSWRInfinite 乐观更新不生效修复
问题根因
配置了optimisticData却不触发即时渲染的核心原因有两个:
- 生成乐观更新数据时直接修改了原缓存对象的引用:代码里直接执行
--comment._count.likedBy、comment.likedByIds.push()这类可变操作,已经污染了SWR持有的原状态,后续哪怕返回浅拷贝的对象,SWR的浅比较逻辑会判定状态未发生实质变更,不会触发即时重渲染。 - 普通
useSWR的缓存是单值结构,而useSWRInfinite的缓存是分页嵌套的二维数组结构,对引用变更的检测更严格,直接修改嵌套属性的写法在单值场景下可能碰巧生效,但在分页场景下会被状态比对拦截。
修复方案
把乐观数据生成逻辑改成全程不可变更新,绝对不要直接修改原data下的任何嵌套对象,同时补充错误回滚配置避免请求失败后UI状态异常,修复后的完整代码如下:
const { data, error, isValidating, mutate, size, setSize } = useSWRInfinite<CommentType[]>( (index) => `/api/comment?postId=${postId}¤tPage=${index + 1}`, fetcher, { revalidateFirstPage: false } ); const likeCommentHandler = async (commentId: string, dislike: boolean) => { // 所有修改基于拷贝生成新值,不触碰原状态引用 const optimisticData = data?.map((comments) => comments.map((comment) => { if (comment.id !== commentId) return comment; // 针对目标评论生成全新对象,所有嵌套属性都做拷贝 return { ...comment, _count: { ...comment._count, likedBy: dislike ? comment._count.likedBy - 1 : comment._count.likedBy + 1 }, likedByIds: dislike ? comment.likedByIds.filter(id => id !== session!.user.id) : [...comment.likedByIds, session!.user.id] } }) ); mutate( async (currentData) => { const { data: result } = await axios.post("/api/likeComment", { commentId, userId: session?.user.id, dislike, }); return currentData?.map((comments) => comments.map((comment) => comment.id === result.comment.id ? result.comment : comment ) ); }, { optimisticData, revalidate: false, populateCache: true, // 请求失败自动回滚到原状态,避免UI和服务端数据不一致 rollbackOnError: true } ); };
额外注意事项
- 所有SWR相关的状态更新都要遵循不可变数据原则,任何嵌套属性的修改都要生成对应的新对象、新数组,不要直接修改原状态的属性值。
- 如果后续需要跨页更新评论,只要保证
optimisticData和异步更新返回值的二维数组结构,和原缓存的分页长度、顺序完全一致,就可以正常触发渲染。
内容的提问来源于stack exchange,提问作者Janghan Park
相关产品推荐
相关产品推荐

