如何使用Apollo的useQuery并发执行多个GraphQL查询
批量处理Apollo useQuery多日期范围查询的最优方案
问题背景
现有可正常运行的Apollo useQuery查询示例,仅处理dates数组的第一个日期范围:
const { data, loading, error } = useQuery(query, { variables: { fromDate: dates[0][0], toDate: dates[0][1] } }); if (loading) return "Loading..."; if (error) return <pre>{error.message}</pre> if (data) console.log(data.token) return <div> <p>{data.token[0].name}</p> </div>
dates数组包含多个双元素日期范围:
const dates = [ ["2022-07-01T00:00:00+00:00", "2022-07-16T00:00:00+00:00"], ["2022-07-16T00:00:00+00:00", "2022-08-01T00:00:00+00:00"], // July ["2022-08-01T00:00:00+00:00", "2022-08-16T00:00:00+00:00"], ["2022-08-16T00:00:00+00:00", "2022-09-01T00:00:00+00:00"], // August ["2022-09-01T00:00:00+00:00", "2022-09-16T00:00:00+00:00"]]
由于无法循环调用useQuery,尝试Promise.all方案触发React错误(Object not valid as a React child),同时不想手动编写多份useQuery,需最优实现方式。
解决方案
方法一:修改GraphQL查询(最优)
如果后端支持,直接扩展查询以接受日期范围数组作为变量,一次性获取所有结果,仅需一次useQuery调用,效率最高。
示例查询定义
# 自定义输入类型(需后端同步支持) input DateRangeInput { fromDate: String! toDate: String! } # 修改后的查询 query GetTokensByDateRanges($dateRanges: [DateRangeInput!]!) { tokensByDateRanges(dateRanges: $dateRanges) { dateRange { fromDate toDate } tokens { name } } }
React组件代码
const { data, loading, error } = useQuery(GET_TOKENS_BY_DATE_RANGES, { variables: { dateRanges: dates.map(([from, to]) => ({ fromDate: from, toDate: to })) } }); if (loading) return "Loading..."; if (error) return <pre>{error.message}</pre>; return ( <div> {data.tokensByDateRanges.map(({ dateRange, tokens }, index) => ( <div key={index}> <h4>日期范围: {dateRange.fromDate} 至 {dateRange.toDate}</h4> <p>{tokens[0]?.name}</p> </div> ))} </div> );
方法二:自定义子组件循环渲染
创建独立子组件封装单个日期范围的useQuery逻辑,父组件循环渲染子组件。此方案符合React Hooks规则,避免循环调用钩子。
子组件代码
const TokenDateRange = ({ fromDate, toDate }) => { const { data, loading, error } = useQuery(query, { variables: { fromDate, toDate } }); if (loading) return <p>加载中...</p>; if (error) return <pre>{error.message}</pre>; return ( <div> <p>{data.token[0].name}</p> </div> ); };
父组件调用
return ( <div> {dates.map(([from, to], index) => ( <TokenDateRange key={index} fromDate={from} toDate={to} /> ))} </div> );
方法三:useLazyQuery结合useEffect批量触发
使用useLazyQuery获取查询函数,在useEffect中批量触发所有查询,通过状态统一管理结果、加载和错误状态。
示例代码
import { useLazyQuery } from '@apollo/client'; import { useState, useEffect } from 'react'; const YourComponent = () => { const [getTokenData] = useLazyQuery(query); const [allData, setAllData] = useState([]); const [isLoading, setIsLoading] = useState(true); const [errors, setErrors] = useState([]); useEffect(() => { let isMounted = true; const fetchAllData = async () => { // 批量触发所有查询 const queryPromises = dates.map(([from, to]) => getTokenData({ variables: { fromDate: from, toDate: to } }) .then(result => ({ range: [from, to], data: result.data, error: result.error })) ); const results = await Promise.all(queryPromises); if (!isMounted) return; // 分离成功结果和错误 const successResults = results.filter(item => item.data); const errorResults = results.filter(item => item.error); setAllData(successResults); setErrors(errorResults); setIsLoading(false); }; fetchAllData(); return () => { isMounted = false; }; }, []); if (isLoading) return "全部加载中..."; if (errors.length > 0) { return ( <div> <h3>部分查询失败:</h3> {errors.map((err, index) => ( <pre key={index}> 范围: {err.range.join(' 至 ')} 错误: {err.error.message} </pre> ))} </div> ); } return ( <div> {allData.map(({ data, range }, index) => ( <div key={index}> <p>范围: {range.join(' 至 ')} - {data.token[0].name}</p> </div> ))} </div> ); };
内容的提问来源于stack exchange,提问作者ep84
相关产品推荐
相关产品推荐

