React Query切换旧查询键重复请求问题及解决咨询
我封装了一个基于React Query useQuery的自定义钩子,传入对象作为参数和查询键。修改对象时会触发新请求,但切回之前的对象状态时也会重复发送请求,不符合缓存复用的预期:
const useFetchUsers = options => { const { query = {} } = options return useQuery({ queryKey: ['users', query], queryFn: () => getUsers({ ...query }), staleTime: 30 * 1000, gcTime: 30 * 1000, }) }
场景说明:
query是React状态,点击按钮会把表单信息赋值给query。初始query为{ page : 1 , ...rest },修改为{ page : 2 , ...rest }时触发新请求,这符合预期;但切回第一页的query对象时,却再次发送请求,我希望React Query能复用之前的缓存。
更新:次日测试相同代码时,缓存功能莫名正常工作了,原因未知。
原因分析
React Query的查询键(queryKey)是通过值的引用判断是否为同一查询的。你切回第一页时,即便对象内容和初始query一致,但如果是新创建的对象(比如通过setQuery({page:1, ...rest})生成了新实例),它的引用和原初始query不同,React Query会判定这是全新查询键,因此重新发起请求。
至于次日测试恢复正常,可能的原因:
- 两次切换时使用了同一个query对象引用
- 两次切换间隔在30秒内(你的gcTime设置),缓存还未被垃圾回收,刚好命中缓存
解决方案
要让React Query正确识别内容相同的query对象,核心是让内容一致的query对应相同的查询键标识,常用两种方法:
方法1:将query对象序列化为稳定字符串作为查询键
把query对象转为JSON字符串,内容相同的对象会生成相同字符串,React Query就能识别为同一查询键:
const useFetchUsers = options => { const { query = {} } = options // 序列化query为字符串,确保内容一致则键一致 const serializedQuery = JSON.stringify(query) return useQuery({ queryKey: ['users', serializedQuery], queryFn: () => getUsers({ ...query }), staleTime: 30 * 1000, gcTime: 30 * 1000, }) }
方法2:复用内容相同的query对象引用
更新query状态时,先判断新参数和当前内容是否一致,一致则复用原对象,避免创建新引用:
// 示例:更新query状态的逻辑 const handleSwitchPage = (page) => { const newQuery = { ...currentQuery, page } // 比较内容是否一致,一致则复用原对象 if (JSON.stringify(newQuery) !== JSON.stringify(currentQuery)) { setQuery(newQuery) } else { setQuery(currentQuery) } }
额外提示
如果query对象结构复杂,JSON.stringify可能存在性能问题或序列化顺序问题(比如键顺序不同但内容相同的对象,序列化结果不同),可以用object-hash这类专门的对象哈希库生成稳定哈希值,作为查询键的一部分,确保内容相同的对象生成相同哈希。
内容的提问来源于stack exchange,提问作者hamid-beigi

