React useQueries实现依赖前置请求的多GET请求遇无限循环问题
问题描述
我需要调用Wikipedia API获取页面数据,核心需求是每次请求需使用前一次请求返回的continue参数(如下方返回结果中的grncontinue值)作为新请求的参数:
"batchcomplete": "", "continue": { "grncontinue": "0.117298963004|0.117302224597|46297018|0", "continue": "grncontinue||" }
我尝试了多种实现方式,以下是最新的尝试代码:
const results = useQueries([ { queryKey: [`getRandomWikis-${count}`], queryFn: async () => { const res = await axios.get(url + endpoint); setEndpoint(`&grncontinue||=${res.data?.data?.continue?.grncontinue}`); setCount((prev) => prev + 1); setAllData((prev) => [...prev, res.data?.data?.query?.pages]); return res; }, enabled: isLocalStorageEmpty, }, { queryKey: [`getRandomWikis-${count}`], queryFn: async () => { const res = await axios.get(url + endpoint); setEndpoint(`&grncontinue||=${res.data?.data?.continue?.grncontinue}`); setCount((prev) => prev + 1); setAllData((prev) => [...prev, res.data?.data.query.pages]); return res; }, enabled: isLocalStorageEmpty && count > 1, }, { queryKey: [`getRandomWikis-${count}`], queryFn: async () => { const res = await axios.get(url + endpoint); setEndpoint(`&grncontinue||=${res.data?.data?.continue?.grncontinue}`); setCount((prev) => prev + 1); setAllData((prev) => [...prev, res.data?.data.query.pages]); return res; }, enabled: isLocalStorageEmpty && count > 2, }, ]); results[2].data?.data && console.log(allData);
目前出现了无限请求的问题,组件多次重渲染直至崩溃,且控制台无日志输出。想请教:我是否错误使用了useQueries?或者有其他可支持更多请求的实现方案?
问题分析与解决方案
你错误使用useQueries的核心原因
- 状态更新触发无限重渲染:每个query的
queryKey依赖count,而queryFn里又调用setCount更新状态,导致组件重渲染后,queryKey变化,useQueries会重新执行请求,进而再次更新count,形成无限循环。 - enabled条件逻辑错误:第二个query的
enabled是count>1,当count更新到2时,第二个query触发,又会把count改成3,此时第三个query的enabled条件count>2满足,触发第三个query,count变成4,而此时第一个query的enabled还是isLocalStorageEmpty(假设为true),又会重新执行第一个query,因为queryKey变成了getRandomWikis-4,再次触发状态更新,循环不止。 - endpoint参数格式错误:你拼接的
&grncontinue||=${...}是错误的,正确的参数格式应该是&grncontinue=${res.data.data.continue.grncontinue},不需要加||,continue字段里的"grncontinue||"是API返回的continue指令,不是参数名。
正确的实现方案
方案1:使用useInfiniteQuery(推荐)
React Query的useInfiniteQuery就是专门处理这种需要分页/续传的请求场景,自动管理continue参数,无需手动维护count和endpoint:
import { useInfiniteQuery } from '@tanstack/react-query'; const fetchWikiPages = async ({ pageParam = '' }) => { const baseUrl = 'https://en.wikipedia.org/w/api.php?action=query&generator=random&grnnamespace=0&grnlimit=2&prop=info&inprop=url&format=json'; const url = pageParam ? `${baseUrl}&grncontinue=${pageParam}` : baseUrl; const res = await axios.get(url); return { pages: Object.values(res.data.query.pages), nextPageParam: res.data.continue?.grncontinue }; }; const { data, fetchNextPage, hasNextPage, isLoading } = useInfiniteQuery({ queryKey: ['randomWikis'], queryFn: fetchWikiPages, getNextPageParam: (lastPage) => lastPage.nextPageParam, enabled: isLocalStorageEmpty, }); // 合并所有批次的页面数据 const allData = data?.pages.flatMap(batch => batch) || []; // 自动加载下一批(可选) // useEffect(() => { // if (hasNextPage && !isLoading) fetchNextPage(); // }, [hasNextPage, isLoading, fetchNextPage]);
方案2:手动维护请求逻辑(适合简单场景)
如果不想用useInfiniteQuery,可以用useEffect结合状态管理,避免无限循环:
import { useState, useEffect } from 'react'; import axios from 'axios'; const [allData, setAllData] = useState([]); const [nextContinue, setNextContinue] = useState(''); const [isLoading, setIsLoading] = useState(false); const [hasMore, setHasMore] = useState(true); const fetchBatch = async () => { if (!hasMore || isLoading) return; setIsLoading(true); const baseUrl = 'https://en.wikipedia.org/w/api.php?action=query&generator=random&grnnamespace=0&grnlimit=2&prop=info&inprop=url&format=json'; const url = nextContinue ? `${baseUrl}&grncontinue=${nextContinue}` : baseUrl; try { const res = await axios.get(url); setAllData(prev => [...prev, ...Object.values(res.data.query.pages)]); setNextContinue(res.data.continue?.grncontinue || ''); setHasMore(!!res.data.continue?.grncontinue); } catch (err) { console.error('请求失败', err); setHasMore(false); } finally { setIsLoading(false); } }; // 初始加载 useEffect(() => { if (isLocalStorageEmpty) fetchBatch(); }, [isLocalStorageEmpty]); // 手动触发加载更多 // <button onClick={fetchBatch} disabled={!hasMore || isLoading}>加载更多</button>
额外注意事项
- 不要在queryFn里直接调用
setState,React Query的queryFn应该只负责获取数据,状态更新交给Query自身的回调或者useEffect处理。 - 确保API参数格式正确,
grncontinue是直接作为参数名使用,不需要拼接多余的||。
内容的提问来源于stack exchange,提问作者Jonathan Ribak
相关产品推荐
相关产品推荐

