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
相关产品推荐
相关产品推荐

