使用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
相关产品推荐
相关产品推荐

