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

