使用Apollo时多Lazy Query轮询仅生效最后一个的解决方法
解决Apollo多个带轮询的查询同时运行的问题
问题原因
你使用的useLazyQuery返回的loadData函数绑定到同一个钩子实例,每次调用loadData都会覆盖当前钩子的查询配置(包括pollInterval),同时终止之前的轮询任务。循环结束后,只有最后一次调用的查询会持续轮询,之前的查询全部被终止,这就是你只看到最后一个条目重复输出的原因。
解决方案
方案1:将每个查询封装为独立组件
把单个条目的查询逻辑拆成独立组件,每个组件内部使用专属的useLazyQuery,让每个查询拥有独立的轮询上下文,避免互相干扰。
示例代码:
// 单个数据查询组件 const DataPollingItem = ({ dataId, idx }: { dataId: string; idx: string }) => { const [loadData] = useLazyQuery(getData, { pollInterval: 3000, notifyOnNetworkStatusChange: true, onCompleted: () => { console.log('loaded', idx); void onboardData(idx, dataId); }, onError: (e) => { console.log('error', idx); }, }); // 组件挂载时触发初始查询 useEffect(() => { void loadData({ variables: { id: dataId } }); }, [dataId, loadData]); return null; // 无需渲染UI内容 }; // 父组件中调用 const ParentComponent = () => { // 假设yourData是你的目标数据集合 const yourData = {/* 你的数据内容 */}; return ( <> {Object.entries(yourData).map(([idx, item]) => ( <DataPollingItem key={idx} dataId={item.id} idx={idx} /> ))} </> ); };
方案2:手动管理轮询(不依赖useLazyQuery的pollInterval)
直接使用Apollo Client的client.query方法,为每个条目单独设置定时器实现轮询,完全掌控每个查询的生命周期。
示例代码:
import { useApolloClient } from '@apollo/client'; const YourComponent = () => { const client = useApolloClient(); // 用ref存储每个条目的定时器,方便后续清除 const timersRef = useRef<Record<string, NodeJS.Timeout>>({}); const startPolling = async (dataEntries: [string, any][]) => { // 先清除之前的定时器,避免重复轮询 Object.values(timersRef.current).forEach(timer => clearInterval(timer)); timersRef.current = {}; for (const [idx, item] of dataEntries) { const dataId = item.id; // 定义单次查询逻辑 const runQuery = async () => { try { await client.query({ query: getData, variables: { id: dataId }, }); console.log('loaded', idx); void onboardData(idx, dataId); // 如果查询成功,可选择停止该条目的轮询 // clearInterval(timersRef.current[idx]); } catch (e) { console.log('error', idx); } }; // 立即执行一次查询,再启动轮询 await runQuery(); timersRef.current[idx] = setInterval(runQuery, 3000); } }; // 组件卸载时清除所有定时器 useEffect(() => { return () => { Object.values(timersRef.current).forEach(timer => clearInterval(timer)); }; }, []); return ( <button onClick={() => startPolling(Object.entries(yourData))}> 启动轮询 </button> ); };
关键说明
- 方案1遵循React钩子规则,实现简单,适合逻辑不复杂的场景。
- 方案2灵活性更高,支持自定义轮询启停逻辑(比如查询成功后停止该条目轮询),适合复杂业务需求。
内容的提问来源于stack exchange,提问作者Casey
相关产品推荐
相关产品推荐

