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

React Suspense边界实现后,如何在事件触发后重新获取API数据?

问题描述

我是React和JS新手,正在尝试给一个需要从后端API获取数据的组件实现React Suspense边界。我参考了一个示例里的fetchData和wrapPromise函数。

wrapPromise函数

/**
 * 包装Promise使其能配合React Suspense使用
 * @param {Promise} promise 要处理的Promise
 * @returns {Object} 兼容Suspense的响应对象
 */
function wrapPromise(promise) {
  let status = "pending";
  let response;

  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 = () => {
    const result = handler[status] ? handler[status]() : handler.default();
    return result;
  };

  return { read };
}

export default wrapPromise;

fetchData函数

/**
 * 用wrapPromise包装Axios请求
 * @param {string} url 请求地址
 * @returns {Object} 包装后的Promise资源
 */
function fetchData(url) {
  const promise = axios.get(url).then(({ data }) => data);

  return wrapPromise(promise);
}

export default fetchData;

组件代码

import fetchData from "../../api/fetchData";

const resource = fetchData("http://localhost:3001/recipes");
...

export default function RecipeTableMain() {
  const data = resource.read(); // 调用wrapPromise返回的read方法

... MUI DataGrid相关渲染代码
}

我的问题

数据只会在组件首次渲染(页面刷新后)时获取一次。我希望在删除或创建新食谱后能重新获取数据,但不知道怎么在现有代码里实现。我觉得自己可能对React组件外部代码的执行逻辑理解有问题。


解决方案

核心问题是:resource在组件外部声明,只会在模块首次加载时执行一次,组件重新渲染时不会重复调用fetchData。要实现重新获取,需把资源创建逻辑放到组件内部,用状态触发更新。

方法1:用useState触发重新获取

添加状态变量refreshKey,更新该变量即可让组件重新创建新的请求资源:

import { useState } from "react";
import fetchData from "../../api/fetchData";

export default function RecipeTableMain() {
  // 用refreshKey触发资源重新创建
  const [refreshKey, setRefreshKey] = useState(0);

  // refreshKey变化时,重新调用fetchData生成新资源
  const resource = fetchData(`http://localhost:3001/recipes?key=${refreshKey}`);
  const data = resource.read();

  // 删除/创建食谱后调用此函数触发刷新
  const handleRecipeUpdated = () => {
    setRefreshKey(prev => prev + 1);
  };

  // ... MUI DataGrid渲染代码,将handleRecipeUpdated绑定到对应操作按钮
}

给URL加key参数是为了避免浏览器缓存,也可以省略,但需确保每次fetchData调用都会发起新请求。

方法2:封装成自定义Hook(推荐)

把Suspense数据获取逻辑封装成可复用的自定义Hook,自带刷新能力:

// useSuspenseData.js
import { useState, useMemo } from "react";
import fetchData from "./fetchData";

export function useSuspenseData(url) {
  const [refreshKey, setRefreshKey] = useState(0);

  // 仅当url或refreshKey变化时,重新创建资源
  const resource = useMemo(() => {
    const uniqueUrl = `${url}?key=${refreshKey}`;
    return fetchData(uniqueUrl);
  }, [url, refreshKey]);

  const data = resource.read();

  return {
    data,
    refresh: () => setRefreshKey(prev => prev + 1)
  };
}

组件中使用:

import { useSuspenseData } from "./useSuspenseData";

export default function RecipeTableMain() {
  const { data, refresh } = useSuspenseData("http://localhost:3001/recipes");

  // 删除/创建食谱后调用refresh触发刷新
  const handleRecipeUpdated = () => {
    refresh();
  };

  // ... MUI DataGrid渲染代码
}

补充说明

React模块的顶层代码(如你之前写的const resource = fetchData(...))只会在模块第一次被导入时执行一次,组件后续重新渲染不会重复运行这段代码。只有组件函数体内部的代码会在每次渲染时执行,因此把资源创建逻辑放到组件内部,配合状态变化就能实现重新获取数据的需求。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 19:56:58