如何让react-query缓存仅用ID作为主键,避免slug与ID重复存储?
解决React Query中Slug转ID后的缓存主键优化问题
核心思路
要实现单份缓存+ID优先主键,关键是把「slug→ID映射」和「文章数据缓存」拆分成两个关联的查询,让slug仅作为获取ID的入口,最终用ID维护唯一的文章缓存条目,避免重复存储。
具体实现方案
封装一个自定义Hook,通过两步查询完成需求:先通过slug获取并缓存ID映射,再用ID读取/缓存文章数据,全程用声明式逻辑替代命令式缓存更新。
import { useQuery, QueryClient } from '@tanstack/react-query'; // 基础API请求函数 const fetchPost = async (identifier: string) => { const response = await fetch(`/posts/${identifier}`); if (!response.ok) throw new Error('Failed to fetch post'); return response.json(); }; export const usePostBySlug = (slug: string) => { const queryClient = useQueryClient(); // 1. 查询slug对应的ID映射,同时初始化ID主键的文章缓存 const slugToIdQuery = useQuery({ queryKey: ['postSlugMap', slug], queryFn: async () => { // 调用API获取文章数据 const post = await fetchPost(slug); // 将文章数据存入ID主键的缓存(核心:确保唯一存储) queryClient.setQueryData(['posts', { id: post.id }], post); // 返回ID作为映射结果 return post.id; }, staleTime: Infinity, // ID映射长期有效,除非主动更新 }); // 2. 拿到ID后,查询文章(直接从缓存读取,不会重复请求API) const postQuery = useQuery({ queryKey: ['posts', { id: slugToIdQuery.data }], queryFn: () => fetchPost(slugToIdQuery.data!), enabled: !!slugToIdQuery.data, // 只有获取到ID后才执行 staleTime: 5 * 60 * 1000, // 按需设置文章缓存时效 }); // 合并两个查询的状态,对外提供统一的返回值 return { ...postQuery, isLoading: slugToIdQuery.isLoading || postQuery.isLoading, error: slugToIdQuery.error || postQuery.error, }; };
方案优势
- 无重复缓存:文章数据仅以
['posts', { id: postId }]作为主键存储,slug仅存ID映射,避免冗余 - ID优先:完全基于不可变的ID维护缓存,即使slug后续变更,只需更新
postSlugMap的映射即可,不影响核心缓存 - 声明式逻辑:通过
enabled控制查询依赖关系,替代手动命令式缓存更新,代码更易维护 - 自动复用缓存:如果其他组件直接用ID调用
useQuery(['posts', { id: xxx }]),会直接命中已有的缓存,无需重复请求
额外优化:处理slug变更场景
如果业务中存在slug修改的情况,只需在文章更新后,调用以下代码更新映射即可:
// 假设更新后拿到新slug和旧slug、文章ID queryClient.setQueryData(['postSlugMap', newSlug], postId); queryClient.removeQueries(['postSlugMap', oldSlug]); // 删除旧映射,避免误用
内容的提问来源于stack exchange,提问作者Jeremy Bernier
相关产品推荐
相关产品推荐

