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

如何将两个React Query接口请求结果合并到同一查询缓存中?

React Query 合并两个分页接口缓存到同一键的方案

要实现将两个接口的分页数据合并到同一个查询缓存键下,同时避免覆盖或请求被阻止的问题,核心思路是用不同的查询键触发各自请求,再手动合并数据到目标缓存,具体方案如下:

1. 为两个接口设置独立的查询键

给两个分页接口分配带标识的唯一查询键(包含分页参数),确保各自的请求能正常触发,不会被对方的缓存拦截:

import { useQuery, useQueryClient } from '@tanstack/react-query';

// 获取全部记录的分页查询
const useAllRecords = (page) => {
  return useQuery(
    ["records", "all", page], // 唯一键:主标识+类型+分页
    () => fetch(`/records/?page=${page}`).then(res => res.json()),
    { keepPreviousData: true } // 分页切换时保留上一页数据,避免空白
  );
};

// 获取未关联所有者记录的分页查询
const useUnownedRecords = (page) => {
  const queryClient = useQueryClient();
  
  return useQuery(
    ["records", "unowned", page], // 唯一键:主标识+类型+分页
    () => fetch(`/records/unowned?page=${page}`).then(res => res.json()),
    {
      keepPreviousData: true,
      // 请求成功后合并数据到目标缓存
      onSuccess: (newPageData) => {
        // 读取现有合并缓存
        const existingMerged = queryClient.getQueryData(["records"]) || [];
        // 合并新数据并去重(假设记录有唯一id字段)
        const merged = [...existingMerged, ...newPageData].filter(
          (item, index, self) => index === self.findIndex(r => r.id === item.id)
        );
        // 更新目标缓存
        queryClient.setQueryData(["records"], merged);
      }
    }
  );
};

2. 在组件中使用并读取合并后的缓存

在业务组件中调用两个分页查询,直接通过queryClient.getQueryData获取合并后的全部记录:

import { useState } from 'react';
import { useQueryClient } from '@tanstack/react-query';

const RecordList = () => {
  const queryClient = useQueryClient();
  const [allPage, setAllPage] = useState(1);
  const [unownedPage, setUnownedPage] = useState(1);

  // 触发两个分页请求
  const { isLoading: isLoadingAll } = useAllRecords(allPage);
  const { isLoading: isLoadingUnowned } = useUnownedRecords(unownedPage);

  // 获取合并后的全部记录
  const mergedRecords = queryClient.getQueryData(["records"]) || [];

  return (
    <div>
      <button 
        onClick={() => setAllPage(prev => prev + 1)}
        disabled={isLoadingAll}
      >加载更多全部记录</button>
      <button 
        onClick={() => setUnownedPage(prev => prev + 1)}
        disabled={isLoadingUnowned}
      >加载更多未关联记录</button>
      
      <ul>
        {mergedRecords.map(record => (
          <li key={record.id}>{record.title}</li>
        ))}
      </ul>
    </div>
  );
};

3. 进阶:自动同步合并缓存(可选)

如果希望合并后的缓存能自动监听两个子查询的更新,可封装一个专门的合并查询hook:

const useMergedRecords = () => {
  const queryClient = useQueryClient();
  
  return useQuery(["records"], () => {
    // 读取所有已缓存的全部记录分页数据
    const allPages = queryClient.getQueriesData(["records", "all"])
      .flatMap(([_, data]) => data || []);
    // 读取所有已缓存的未关联记录分页数据
    const unownedPages = queryClient.getQueriesData(["records", "unowned"])
      .flatMap(([_, data]) => data || []);
    // 合并并去重
    return [...new Map([...allPages, ...unownedPages].map(r => [r.id, r])).values()];
  }, {
    // 通过子查询的更新时间戳触发缓存刷新
    queryKeyHashFn: () => {
      const allUpdateTimes = queryClient.getQueryCache().getAll()
        .filter(q => q.queryKey[0] === "records" && q.queryKey[1] === "all")
        .map(q => q.state.dataUpdatedAt);
      const unownedUpdateTimes = queryClient.getQueryCache().getAll()
        .filter(q => q.queryKey[0] === "records" && q.queryKey[1] === "unowned")
        .map(q => q.state.dataUpdatedAt);
      return JSON.stringify([...allUpdateTimes, ...unownedUpdateTimes]);
    },
    refetchInterval: false
  });
};

// 使用方式
const { data: mergedRecords } = useMergedRecords();

关键注意点

  • 去重逻辑:必须添加去重处理,避免两个接口返回重复记录导致缓存冗余。
  • 分页参数:子查询的键必须包含分页参数,确保每一页的请求都能被单独缓存,不会互相覆盖。
  • 缓存失效:如果后续需要更新合并后的缓存,可调用queryClient.invalidateQueries(["records"])触发重新合并。

内容的提问来源于stack exchange,提问作者ABMagil

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 18:22:42