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

React应用中React Query动态增量查询的优化方案咨询

解决React Query分页的进度追踪与缓存复用问题

针对你遇到的两个痛点(无法追踪单请求进度、刷新后需重新请求全部),可以通过拆分单个分页请求为独立的React Query查询来解决,同时用状态管理进度和分页链路,完美适配动态返回后续URL的场景,具体实现如下:


核心逻辑

放弃一次性串行请求的封装,改用:

  • 每个分页请求用独立的queryKey,让React Query自动缓存单页数据,刷新后直接读取已缓存的页面
  • 用状态记录已加载的页面URL、已加载数据量、总数据量(或已完成请求数),实现进度计算

具体代码实现

1. 封装单页请求函数

// 单个分页请求的基础函数,接收页面URL,返回该页数据
const fetchReviewPage = async (pageUrl) => {
  const response = await fetch(pageUrl || '/reviews');
  if (!response.ok) throw new Error('请求失败');
  return response.json();
};

2. 组件内实现动态分页、缓存与进度追踪

import { useQuery, useQueries } from '@tanstack/react-query';
import { useState, useEffect } from 'react';

const ReviewList = () => {
  // 状态管理:已加载的页面URL列表、下一页URL、已加载数据量、总数据量
  const [loadedPageUrls, setLoadedPageUrls] = useState(['/reviews']);
  const [nextPageUrl, setNextPageUrl] = useState(null);
  const [loadedItemsCount, setLoadedItemsCount] = useState(0);
  const [totalItemsCount, setTotalItemsCount] = useState(0);

  // 用useQueries批量处理所有已加载页面的查询
  // 每个页面对应独立的queryKey,React Query会自动缓存单页数据
  const pageQueries = useQueries({
    queries: loadedPageUrls.map(url => ({
      queryKey: ['review-page', url], // 唯一标识单页请求,用于缓存
      queryFn: () => fetchReviewPage(url),
      staleTime: 5 * 60 * 1000, // 设置5分钟缓存有效期,可按需调整
    }))
  });

  // 首次请求完成后,初始化总数据量和下一页URL
  useEffect(() => {
    const firstPageData = pageQueries[0]?.data;
    if (firstPageData) {
      setNextPageUrl(firstPageData.nextPageUrl);
      setTotalItemsCount(firstPageData.totalItems); // 假设API返回总数据量totalItems
      setLoadedItemsCount(firstPageData.data.length);
    }
  }, [pageQueries[0]?.data]);

  // 监听后续页面加载完成,更新进度和下一页URL
  useEffect(() => {
    // 找到最新完成的请求
    const latestCompletedQuery = pageQueries
      .filter(query => query.isSuccess)
      .pop();
    
    if (latestCompletedQuery?.data) {
      const latestData = latestCompletedQuery.data;
      setNextPageUrl(latestData.nextPageUrl);
      setLoadedItemsCount(prev => prev + latestData.data.length);
      
      // 如果有未加载的下一页,自动加入请求列表
      if (latestData.nextPageUrl && !loadedPageUrls.includes(latestData.nextPageUrl)) {
        setLoadedPageUrls(prev => [...prev, latestData.nextPageUrl]);
      }
    }
  }, [pageQueries, loadedPageUrls]);

  // 计算进度百分比:有总数据量用精确值,无则用已完成请求数做近似值
  const progress = totalItemsCount 
    ? Math.round((loadedItemsCount / totalItemsCount) * 100) 
    : pageQueries.length * 10;

  // 合并所有已加载页面的数据
  const allReviews = pageQueries
    .filter(query => query.isSuccess)
    .flatMap(query => query.data.data);

  return (
    <div>
      {/* 进度条 */}
      <div style={{ width: '100%', height: '8px', backgroundColor: '#eee', borderRadius: '4px' }}>
        <div style={{ 
          width: `${progress}%`, 
          height: '100%', 
          backgroundColor: '#2196F3',
          borderRadius: '4px'
        }} />
      </div>
      {/* 加载状态提示 */}
      {pageQueries.some(query => query.isLoading) && <p>加载中...</p>}
      {/* 渲染所有评论 */}
      <ul style={{ listStyle: 'none', padding: 0 }}>
        {allReviews.map(review => (
          <li key={review.id} style={{ padding: '8px 0', borderBottom: '1px solid #eee' }}>
            {review.content}
          </li>
        ))}
      </ul>
    </div>
  );
};

关键优势

  • 缓存复用:每个分页请求有独立的queryKey,刷新页面后React Query会自动读取已缓存的页面,从上次中断的位置继续加载,无需从头请求
  • 进度追踪:通过loadedItemsCount和totalItemsCount计算精确进度,即使API不返回总数据量,也可以用已完成请求数作为近似进度
  • 灵活控制:可以随时修改逻辑为“手动加载更多”(比如移除自动添加下一页的逻辑,改用按钮触发),或手动清除某页缓存

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 11:01:13