You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的核心原因

  1. 状态更新触发无限重渲染:每个query的queryKey依赖count,而queryFn里又调用setCount更新状态,导致组件重渲染后,queryKey变化,useQueries会重新执行请求,进而再次更新count,形成无限循环。
  2. 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,再次触发状态更新,循环不止。
  3. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 09:15:32