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

使用React Suspense与Axios获取数据时出现无限循环问题

React Suspense 配合Axios出现无限循环问题的解决

问题描述

按照教程实现React Suspense组件中用Axios获取数据,但wrapPromise函数导致无限循环,pending回调被持续调用,无法正常完成数据加载。

相关代码

wrapPromise函数:

function wrapPromise(promise: Promise<any>) {
    let status = 'pending';
    let response : any;
  
    const suspender = promise.then(
      res => {
        status = 'success';
        response = res;
      },
      err => {
        status = 'error';
        response = err;
      },
    );
  
    const handler = {
      pending: () => {
        throw suspender;
      },
      error: () => {
        throw response;
      },
      default: () => response,
    };
  
    const read = () => {
        //@ts-ignore
      const result = handler[status] ? handler[status]() : handler.default();
      return result;
    };
  
    return { read };
  }
  
  export default wrapPromise;

调用逻辑:

const dataFetch = (id: number) => {
    const logsPromise: Promise<Array<Log>> = fetchLogs(id)
    return {
        logs: wrapPromise(logsPromise),
    }
}


function fetchLogs(id: number): any {
    return axios
        .get(`myurl/getlogs?id=${id}`)
        .then((r) => r.data as Array<Log>)
        .catch((err) => console.log(err))
}

const logTable = function logTable({ id }: logProps) {
     return (
            <Suspense
                    fallback={<FallbackComponent />}
                >
                    <LogResult id={id} />
                </Suspense>
        )
}

const LogResult  = function MyLogs({ id }: LogResultProps) {
    let logs: any = dataFetch(id).logs.read() 
    return <LogComponent logs={logs as Array<Log>} />
}

问题原因

每次LogResult组件渲染时,都会调用dataFetch(id),这会创建新的Promise实例和对应的wrapPromise对象。React Suspense捕获到抛出的suspender后会重新渲染组件,而每次渲染又会生成新的pending状态的Promise,导致无限循环。

此外,fetchLogs的catch中仅打印错误但未抛出,导致wrapPromise无法捕获错误状态,可能加剧异常情况的循环。

解决方案

1. 缓存请求结果

用缓存存储已发起请求的wrapPromise实例,避免相同id重复创建新的Promise:

// 新增缓存,存储id对应的请求结果
const requestCache = new Map<number, { logs: { read: () => Array<Log> } }>();

const dataFetch = (id: number) => {
    // 优先从缓存获取,不存在才发起新请求
    if (!requestCache.has(id)) {
        const logsPromise: Promise<Array<Log>> = fetchLogs(id);
        requestCache.set(id, {
            logs: wrapPromise(logsPromise),
        });
    }
    return requestCache.get(id)!;
}

2. 修正错误处理

在fetchLogs的catch中抛出错误,让wrapPromise能正确捕获并设置error状态:

function fetchLogs(id: number): Promise<Array<Log>> {
    return axios
        .get(`myurl/getlogs?id=${id}`)
        .then((r) => r.data as Array<Log>)
        .catch((err) => {
            console.log(err);
            // 抛出错误,使wrapPromise能处理error状态
            throw err;
        });
}

原理说明

缓存确保相同id仅创建一次Promise和wrapPromise实例,当Promise完成(成功/失败)后,status会更新为success或error,后续read()调用不会再抛出suspender,组件能正常渲染,终止循环。

内容的提问来源于stack exchange,提问作者MmM

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 07:30:53