Apollo useSuspenseQuery引发React组件持续重渲染问题
核心原因分析
变量引用不稳定,触发重复查询
你的timeSeriesRequests虽然用useMemo做了缓存,但依赖数组中缺少了timeseriesMetadata。如果timeseriesMetadata是动态生成的(比如作为props传入、或组件每次渲染时重新定义),会导致useMemo的缓存逻辑失效,每次渲染都生成新的数组引用。Apollo的useSuspenseQuery对变量做浅比较,只要引用变化就会触发新查询,进而形成「查询→组件重渲染→生成新变量→再次查询」的循环。fetchPolicy: "no-cache"的放大效应
该策略会完全绕过缓存,每次都发起网络请求。普通Query组件内部可能有额外的变量稳定处理逻辑,而useSuspenseQuery对变量变化更敏感,微小的引用变化都会触发新请求。时间变量的动态性(潜在诱因)
DateTime.now()每次调用都会生成新的时间值,如果useMemo被意外触发(比如依赖项变化),会生成不同的fromDate/toDate,导致查询变量内容完全不同,必然触发新请求。如果父组件中highlightedGeograhy.id频繁变化,也会导致useMemo重新计算,生成新的时间变量。
解决方案
1. 补全useMemo依赖数组
将timeseriesMetadata加入timeSeriesRequests的useMemo依赖中,确保只有当依赖项真正变化时才重新生成变量数组:
const timeSeriesRequests = useMemo(() => { const now = DateTime.now(); return Object.values(timeseriesMetadata).reduce<TimeSeriesRequestInput[]>((acc, value) => { return [ ...acc, { id: locationId, name: value, fromDate: now.toISO(), toDate: now.toISO() }, { id: locationId, name: value, fromDate: now.minus({ years: 5 }).toISO(), toDate: now.toISO() }, ]; }, []); }, [locationId, timeseriesMetadata]); // 新增timeseriesMetadata到依赖数组
2. 稳定时间变量(可选)
如果业务不需要每次请求都使用当前时间,用useRef缓存时间值,避免因时间变化导致变量内容不同:
const nowRef = useRef(DateTime.now()); const timeSeriesRequests = useMemo(() => { return Object.values(timeseriesMetadata).reduce<TimeSeriesRequestInput[]>((acc, value) => { return [ ...acc, { id: locationId, name: value, fromDate: nowRef.current.toISO(), toDate: nowRef.current.toISO() }, { id: locationId, name: value, fromDate: nowRef.current.minus({ years: 5 }).toISO(), toDate: nowRef.current.toISO() }, ]; }, []); }, [locationId, timeseriesMetadata]);
3. 调整查询缓存策略(可选)
如果业务允许,将fetchPolicy改为"cache-first"或"cache-and-network",利用缓存避免重复请求:
const { data: { timeSeriesEndPoint: { getTimeSeries: entries }, }, } = useSuspenseQuery<LocationStatsQuery>(locationStatsQuery, { variables: { timeSeriesRequests }, fetchPolicy: "cache-first", // 改用缓存优先策略 });
4. 稳定父组件传入的props
检查父组件中highlightedGeograhy是否频繁生成新对象(即使内容相同),用useMemo缓存该对象,避免子组件不必要的重渲染:
// 在DetailsPanel组件中 const stableHighlightedGeography = useMemo(() => highlightedGeograhy, [highlightedGeograhy?.id]); // 传递稳定的id给子组件 <Suspense fallback={Fallback}> <LocationStats locationId={stableHighlightedGeography.id} initialMeasure={Measure.Capacity} /> </Suspense>
内容的提问来源于stack exchange,提问作者kfc

