You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Query中同一数据对应两个缓存键的正确处理方案咨询

React Query中同一数据对应两个缓存键的正确处理方案咨询

I have a Project type in my app. I need to be able to access it via two separate async function:

  • getProductBySlug
  • getProductById

At the root of the page I use the URL to derive the slug and query for it using a useGetProductBySlugQuery hook (with a queryKey of ['products', productSlug]) but everywhere else in component hierarchy where I need to access a project I use a useProductByIdQuery hook (with a queryKey of ['products', productId]), and there are a variety of other hooks that update projects which all use this queryKey too.

Although storing the same data in the cache at two locations isn't in itself a problem, this also means the same resource is stored in two different locations in the cache. This is problematic because:

  • Any hooks that use the ['products', productId] will not be fired when ['products', productSlug] is updated or invalidated.
  • Any hooks that use the ['products', productSlug] will not be fired when ['products', productId] is updated or invalidated.
  • Using the project returned form useGetProductBySlugQuery to supply the id to useGetProductByIdQuery results in an unnecessary second request to get the same data.

So far there appear to be two solutions to this problem:

  1. Manually update / invalidate both keys in each hook that uses either key. This involves a lot of duplicated logic, and is an obvious potential source of bugs.

  2. Create a special dependent query at the root that will be triggered only when the query using ['projects', projectSlug] succeeds in returning a project. That way the only place there is a dependency on the slug query is at the root of the project, and the rest of the project and queries are completely oblivious to it, meaning there is no need to update the ['projects', projectSlug] key at all.

Something like this:

const useCopyProjectToCacheQuery = (project: Project) => {
  const queryClient = useQueryClient()
  return useQuery({
    queryKey: projectKeyFactory.project(project?.id),
    queryFn: async () => {
      // Check the cache for data stored under the project's id
      const projectDataById = await queryClient.getQueryData(
        projectKeyFactory.project(project.id)
      )

      return isNil(projectDataById)
        ? // If the data isn't found, copy the data from the project's slug key
          await queryClient.getQueryData<ProjectWithRelations>(
            projectKeyFactory.project(project.id)
          )
        : // Otherwise get it from the server
          await findProjectByIdAction(project.id)
    },
    enabled: isNotNil(project),
  })
}

This will populate the ['projects', projectId] key with the project from the ['projects', projectSlug] key when it is first populated, then will run a normal query on subsequent calls.

The second option appears to be working fine (although I end up with a cache entry for the key ['projects', null] created while project is null), but this seems like a really clumsy way to solve the problem. My next thought was to subscribe to the QueryCache and copy the data from each key to the other whenever one of the keys changes, however the docs for QueryCache.subscribe state:

Out of scope mutations to the cache are not encouraged and will not fire subscription callbacks

So what is the correct way to deal with this situation?

备注:内容来源于stack exchange,提问作者Undistraction

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 09:19:34