如何让React Suspense与Pending Promise正常工作并支持重复请求
React Suspense 二次访问不重新请求数据的解决方案
问题核心:当前代码中resource是在组件外部定义的全局单例,首次请求完成后状态变为success,后续调用reset仅重置状态标记,并未重新发起网络请求,导致二次访问组件时直接复用缓存数据。
下面提供两种可行解决思路:
方案一:组件挂载时创建新的 Resource 实例
将resource的创建逻辑移到组件内部,确保每次组件挂载都生成新的请求实例,自然触发重新请求:
修改后的组件代码:
import React from 'react'; import wrapPromise from './wrapPromise'; const dataFetchWithWrapPromise = (url) => { return wrapPromise(window.fetch(url)); }; function Articles() { // 初始化时创建首个请求实例 const [resource, setResource] = React.useState(() => dataFetchWithWrapPromise('http://localhost:3000/data') ); // 组件挂载时重新创建请求实例,确保二次访问发起新请求 React.useEffect(() => { const newResource = dataFetchWithWrapPromise('http://localhost:3000/data'); setResource(newResource); }, []); const data = resource.read(); return ( <> <h1>数据</h1> <pre> {JSON.stringify(data, null, 4)} </pre> </> ); } export default Articles;
方案二:修改 wrapPromise 让 reset 重新发起请求
若需保留全局resource实例,同时支持重置后重新请求,可修改wrapPromise逻辑,让reset不仅重置状态,还触发新的网络请求:
修改后的 wrapPromise 代码:
function wrapPromise(getPromise) { let status = 'pending'; let response; let suspender; // 封装请求初始化逻辑 const initRequest = () => { suspender = getPromise().then( async res => { status = 'success'; response = await res.json(); }, err => { status = 'error'; response = err; }, ); }; // 首次初始化请求 initRequest(); const handler = { pending: () => { throw suspender; }, error: () => { throw response; }, success: () => { return response; }, default: () => { throw suspender; }, }; const read = () => { const result = handler[status] ? handler[status]() : handler.default(); return result; }; const reset = () => { status = 'pending'; response = undefined; initRequest(); // 重置后重新发起请求 }; return { read, reset }; } export default wrapPromise;
对应的组件代码调整:
import React from 'react'; import wrapPromise from './wrapPromise'; // 传入返回Promise的函数,而非直接传入Promise实例 const dataFetchWithWrapPromise = (url) => { return wrapPromise(() => window.fetch(url)); }; // 全局resource实例 const resource = dataFetchWithWrapPromise('http://localhost:3000/data'); function Articles() { const data = resource.read(); // 组件卸载时重置请求,确保下次访问重新发起 React.useEffect(() => { return () => { resource.reset(); }; }, []); return ( <> <h1>数据</h1> <pre> {JSON.stringify(data, null, 4)} </pre> </> ); } export default Articles;
方案选择
- 若需求是每次组件挂载必须重新请求,优先选方案一,逻辑简单直接。
- 若需复用同一resource实例并支持手动重置请求,选方案二,适合有主动刷新需求的场景。
内容的提问来源于stack exchange,提问作者newBike
相关产品推荐
相关产品推荐

