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

如何让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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 01:00:58