如何将从远程 API 获取的初始数据与 React.Suspense 集成?
Posted
技术标签:
【中文标题】如何将从远程 API 获取的初始数据与 React.Suspense 集成?【英文标题】:How to integrate initial data fetching from a remote API, with React.Suspense? 【发布时间】:2021-11-09 21:21:39 【问题描述】:我正在现有样板之上构建一个新的 React 应用程序。它使用延迟加载,结合 React.Suspense。
问题是,就像在大多数 React 应用程序中一样,我需要在每次应用程序加载时从服务器获取一些初始元数据。我们称之为“getAppMetaData”。
那么问题是什么?问题是,虽然 getAppMetaData 处于待处理状态,但我需要提供一些加载器/微调器。这正是 React.Suspense 所做的:它显示了“后备”用户界面。当然,我可以运行一个单独的加载器(实际上可以与回退 UI 相同),但这会产生 UX 问题,加载器的动画在程序之间“重新启动”。
那么,问题是,我如何将其他异步操作“集成”到此暂停中?简而言之:“我的后备 UI 已经显示,而块(来自延迟加载)已加载 - 那么我如何让它也等待 getAppMetaData?”
这是我的路由器:
<ErrorBoundary>
<Suspense fallback=<div className=styles.loader><Loader /></div>>
<Switch>
<ProtectedRoute exact component=Home path="/">
</ProtectedRoute>
<Route path="/lesson">
<Lesson></Lesson>
</Route>
<Route exact path="/login">
<Login />
</Route>
<Route path="/about">
<About />
</Route>
<Route path="*">
<NotFound />
</Route>
</Switch>
</Suspense>
</ErrorBoundary>
React 文档指出,应该为此使用 Relay 库,但我不想为我的 API 调用使用任何特殊库,只是为了克服这个简单问题。它还指出:
如果我不使用 Relay 会怎样?如果你今天不使用 Relay,你可能 必须等待才能真正在应用中尝试 Suspense。迄今为止, 这是我们在生产中测试的唯一实现,并且是 有信心。
我所需要的只是将一个小的初始 API 调用集成到这个过程中。如何做呢? 任何建议将不胜感激。
【问题讨论】:
Suspense
只是一个组件,它捕获一个 Promise 并显示一个回退直到 Promise 被解决,所以你使用 Promise 加载数据并抛出它,这样 Suspense 组件就可以捕获它并显示数据加载时微调器
我明白了,但是我到底应该把这段代码放在哪里呢?我如何确保它被悬念抓住了?如果我有一些组件位于 Suspense 树中,我该如何实际操作呢?如果我从 useEffect 抛出一个承诺,我会得到一个未捕获的错误
【参考方案1】:
我会将 Suspense 的子组件移动到一个新组件中并从该组件中读取数据。
数据将使用自定义函数fetchData
加载,该函数将创建promise 并返回一个包含read
方法的对象,如果数据未准备好,该方法将抛出promise。
function fetchData()
let status = 'pending';
let result;
const promise = fetch('./data.json')
.then(data => data.json())
.then(r =>
status = 'success';
result = r;
)
.catch(e =>
status = 'error';
result = e;
);
return
read()
if (status === 'pending')
throw promise;
else if (status === 'error')
throw result;
else if (status === 'success')
return result;
;
const dataWrapper = fetchData();
function AppBody()
const data = dataWrapper.read();
// you can now manipulate the data
return (
<Switch>
<ProtectedRoute exact component=Home path="/"/>
<Route path="/lesson" component=Lesson />
<Route exact path="/login" component=Login />
<Route path="/about" component=About />
<Route path="*" component=NotFound />
</Switch>
)
function App()
return (
<ErrorBoundary>
<Suspense fallback=<div className=styles.loader><Loader /></div>>
<AppBody/>
</Suspense>
</ErrorBoundary>
);
这是stackblitz example
这段代码的灵感来自react documentationcodesandbox
【讨论】:
哇,这很好用。我一定会学习这段代码并理解其中的机制。谢谢。 您只需要了解Suspense
组件的工作是捕获promise 并显示一个备用组件,直到promise 被解决。
你将如何从这个创建一个自定义的钩子,它可以在任何组件中使用?(都在同一个 Suspense 树下)。可以让我从组件中调用“useSuspense(somePromise)”的东西。
最简单的方法是在自定义钩子中使用useMemo
和[]
来创建promise 并使用read 方法返回对象。更复杂的方法是结合useState
(用于存储承诺)和useMemo
使用 read 方法构建对象,使用此方法您可以通过使用新承诺更新状态来刷新数据。
谢谢。你能解释一下我到底需要记住什么吗?以上是关于如何将从远程 API 获取的初始数据与 React.Suspense 集成?的主要内容,如果未能解决你的问题,请参考以下文章
将从 api 接收到的数据格式化为数组 React Axios
如何将从 API 获取的数组分配给 Vue.js 中的数据属性?