Next.js中SWR缓存旧数据导致筛选/分页切换显示异常及cache.get()返回undefined的问题求助
Next.js中SWR缓存旧数据导致筛选/分页切换显示异常及cache.get()返回undefined的问题求助
我来帮你梳理下问题出在哪,以及怎么一步步解决:
一、为什么cache.get()总是返回undefined?
你这里的核心问题是SWR缓存的key类型不匹配:
- 你在
useSWR里用的是数组类型的key:["dataRequests", token, currentPage] - 但你却把数组转成字符串
JSON.stringify(swrKey)去调用cache.get(),而SWR内部缓存是直接用原数组作为key存储的,根本不会识别字符串格式的key,自然返回undefined。
正确的做法是直接用你传给useSWR的原数组去调用cache.get:
const swrKey = token ? ["dataRequests", token, currentPage] : null; const cachedData = cache.get(swrKey); // 直接传数组key,不要转字符串!
另外要注意:SWR对数组key的匹配是结构相等判断,只要数组元素的内容完全一致,就能匹配到对应的缓存项,不需要额外转格式。
二、你的缓存清除与请求逻辑的问题
你现在的思路是“有缓存就清除,没有就触发请求”,但结合keepPreviousData: true的设置,这个逻辑其实和你想要的效果冲突:
keepPreviousData: true的设计初衷是单纯分页切换时保留旧数据,避免页面空白,但如果是筛选/搜索/排序条件变化,你其实不需要保留旧数据,反而要直接加载新数据。- 你用
GlobalMutate清除缓存的逻辑也有问题:你写的是匹配字符串key,但你的实际key是数组,所以这个过滤条件根本找不到对应的缓存项。
三、正确的解决方案:针对场景优化SWR配置
1. 把所有影响请求结果的参数加入SWR的key
你当前的key只包含了token和currentPage,但筛选条件(filters)、搜索关键词(searchedRequest)、排序规则(sortBy/sortOrder)都是会影响请求结果的参数,必须加入到SWR的key里!不然SWR会把不同筛选条件但同页面/同token的请求当成同一个key,导致缓存完全混乱。
修正后的SWR key应该是这样:
// 把filters转成字符串是因为对象作为数组元素时,结构相等判断会失效(比如两个内容一致的{}会被认为是不同key) const swrKey = token ? ["dataRequests", token, currentPage, searchedRequest, sortBy, sortOrder, JSON.stringify(filters)] : null;
2. 区分分页和筛选场景,动态设置keepPreviousData
我们可以只在单纯分页切换时保留旧数据,而当筛选/搜索/排序条件变化时,直接加载新数据,不保留旧数据:
// 先判断当前是否是单纯的分页变化(需要你自己维护上一次的参数状态,比如用useRef存上一次的筛选/搜索/排序值) const prevParamsRef = useRef({ searchedRequest, sortBy, sortOrder, filters: JSON.stringify(filters) }); const isOnlyPageChange = prevParamsRef.current.searchedRequest === searchedRequest && prevParamsRef.current.sortBy === sortBy && prevParamsRef.current.sortOrder === sortOrder && prevParamsRef.current.filters === JSON.stringify(filters); // 每次渲染更新参数快照 useEffect(() => { prevParamsRef.current = { searchedRequest, sortBy, sortOrder, filters: JSON.stringify(filters) }; }, [searchedRequest, sortBy, sortOrder, filters]); // 然后在SWR配置里动态设置 const { data, mutate, isLoading, isValidating, } = useSWR( swrKey, () => fetchRequests( token, searchedRequest, currentPage, sortBy, sortOrder, filters ), { keepPreviousData: isOnlyPageChange, // 只有单纯分页时才保留旧数据 dedupingInterval: 500, } );
3. 正确的缓存清除方式
如果你还是需要手动清除缓存并重新请求,不要用cache.get()判断,而是直接用全局mutate匹配正确的key类型:
const { mutate: globalMutate } = useSWRConfig(); // 清除所有和dataRequests相关的缓存(匹配数组key的第一个元素) globalMutate( (key) => Array.isArray(key) && key[0] === "dataRequests", undefined, { revalidate: false } ); // 或者更精准:清除当前token下的所有dataRequests缓存 globalMutate( (key) => Array.isArray(key) && key[0] === "dataRequests" && key[1] === token, undefined, { revalidate: false } );
这里的关键是:你的key是数组,所以在全局mutate的过滤函数里要先判断key是数组,再匹配数组元素,而不是去匹配字符串。
四、其他需要注意的细节
- 你同时使用了
useSWR返回的mutate和全局的GlobalMutate,建议统一命名(比如把全局的叫globalMutate),避免变量名冲突导致逻辑混乱。 - 不要在组件渲染根节点直接调用
cache.get()和mutate,这会导致每次组件渲染都执行该逻辑,引发不必要的重渲染和请求,建议把逻辑放到useEffect里,结合状态变化触发。
内容来源于stack exchange
相关产品推荐
相关产品推荐

