React+TS中用useQuery实现指标数据增量更新的方法
实现增量更新指标数据的方案
要实现首次加载10分钟数据、后续每30秒增量拉取并维护内存数据的需求,可以通过TanStack React Query的缓存读取与数据合并逻辑来完成,具体实现如下:
核心思路
- 首次请求:拉取最近10分钟的全量数据。
- 后续增量请求:每次仅拉取最近30秒的新数据。
- 数据合并与清理:将新数据追加到现有缓存中,同时过滤掉超过10分钟的旧数据,保证内存中始终保留最新10分钟的指标。
完整代码实现
import { useQuery, useQueryClient } from '@tanstack/react-query'; import { subMinutes, subSeconds, isAfter, parseISO } from 'date-fns'; // 定义指标数据类型(根据实际API返回结构调整) interface MetricData { timestamp: string; // ISO格式时间字符串 value: number; // 其他字段... } // 假设的API请求函数(替换为实际接口调用) const getMetrics = async ( metricId: string, params: { startAt: string; endAt: string } ): Promise<MetricData[]> => { const response = await fetch( `/api/metrics/${metricId}?startAt=${params.startAt}&endAt=${params.endAt}` ); if (!response.ok) throw new Error('Failed to fetch metrics'); return response.json(); }; // 组件内使用 const MetricComponent = ({ metricId }: { metricId: string }) => { const queryClient = useQueryClient(); const { data: metricData, isLoading, isFetching } = useQuery({ queryKey: ['fetch-metric', metricId], queryFn: async () => { const now = new Date(); // 读取当前缓存中的已有数据 const existingData = queryClient.getQueryData<MetricData[]>(['fetch-metric', metricId]); let newData: MetricData[]; if (!existingData) { // 首次请求:拉取最近10分钟数据 const startAt = subMinutes(now, 10); newData = await getMetrics(metricId, { startAt: startAt.toISOString(), endAt: now.toISOString(), }); } else { // 后续增量请求:拉取最近30秒数据 const startAt = subSeconds(now, 30); newData = await getMetrics(metricId, { startAt: startAt.toISOString(), endAt: now.toISOString(), }); } // 合并新旧数据,过滤掉超过10分钟的旧数据 const tenMinutesAgo = subMinutes(now, 10); const combinedData = [...(existingData || []), ...newData].filter(item => { const itemTimestamp = parseISO(item.timestamp); return isAfter(itemTimestamp, tenMinutesAgo); }); // 去重(避免API返回重复的时间点数据) const uniqueData = Array.from( new Map(combinedData.map(item => [item.timestamp, item])).values() ); // 按时间戳升序排序,保证数据顺序正确 uniqueData.sort((a, b) => parseISO(a.timestamp).getTime() - parseISO(b.timestamp).getTime()); return uniqueData; }, refetchOnWindowFocus: true, refetchInterval: 30000, // 每30秒自动刷新 }); return ( <div> {isLoading ? ( <p>加载中...</p> ) : ( <div> {isFetching && <p>更新数据中...</p>} {/* 渲染指标数据 */} {metricData?.map(metric => ( <div key={metric.timestamp}> <span>{metric.timestamp}</span> <span>{metric.value}</span> </div> ))} </div> )} </div> ); }; export default MetricComponent;
关键细节说明
- 缓存读取:通过
queryClient.getQueryData获取当前缓存的指标数据,判断是否为首次请求。 - 增量请求:后续请求仅拉取最近30秒的数据,减少API传输量。
- 数据清理:使用
date-fns的isAfter过滤掉超过10分钟的旧数据,自动维持10分钟的时间窗口。 - 去重与排序:通过
Map基于时间戳去重,避免重复数据;排序保证数据按时间顺序展示。 - 状态区分:用
isLoading标识首次加载状态,isFetching标识后续增量更新状态,提升用户体验。
内容的提问来源于stack exchange,提问作者Murtuza Z
相关产品推荐
相关产品推荐

