使用 Suspense 加载状态
您可能已经注意到,我们提到使用 usePreloadedQuery
和 useLazyLoadQuery
将从服务器获取的查询中的数据渲染出来,但我们没有详细说明如何在获取数据时渲染加载 UI(例如闪烁)。我们将在这部分内容中介绍。
为了在获取查询时渲染加载状态,我们依赖于 React Suspense。Suspense 是 React 中的一项新功能,允许组件中断或“挂起”渲染,以便等待一些异步资源(例如代码、图像或数据)加载;当一个组件“挂起”时,它会指示 React 该组件还没有“准备好”渲染,只有在它等待的异步资源加载后才会渲染。当资源最终加载时,React 会尝试再次渲染该组件。
此功能对于组件来说非常有用,它可以表达他们渲染所需的异步依赖项(如数据、代码或图像),并允许 React 协调在异步资源可用时跨组件树渲染加载状态。更一般地说,使用 Suspense 使我们能够更好地控制在应用程序首次加载或过渡到不同状态时实现更精心设计的加载状态,并有助于防止意外闪烁加载元素(例如微调器),而加载元素的闪烁通常发生在未明确设计和协调加载序列时。
请注意,这 **并不意味着**“用于数据获取的 Suspense”已准备好进行通用实现和采用。**用于数据获取的 Suspense 的支持、一般指南和使用要求尚未准备好**,React 团队仍在定义未来 React 版本中此指南将是什么。
即使 React 17 中使用 Suspense 时会有一些限制,Relay Hooks 也是稳定的,并且正在朝着支持未来版本的 React 的方向发展。
有关更多信息,请参阅我们的 **Suspense 兼容性** 指南。
使用 Suspense 边界加载回退
当一个组件挂起时,我们需要在组件就绪之前渲染一个回退来代替该组件。为此,我们使用 React 提供的 Suspense
组件。
const React = require('React');
const {Suspense} = require('React');
function App() {
return (
// Render a fallback using Suspense as a wrapper
<Suspense fallback={<LoadingGlimmer />}>
<CanSuspend />
</Suspense>
);
}
Suspense
组件可用于包装任何组件;如果目标组件挂起,Suspense
将渲染提供的回退,直到其所有后代都“就绪”(即直到子树中所有挂起的组件都解析)。通常,回退用于渲染回退加载状态,例如微调器和占位符。
通常,我们应用程序中的不同内容可能会挂起,因此我们可以使用 Suspense
显示加载状态,直到它们被解析。
/**
* App.react.js
*/
const React = require('React');
const {Suspense} = require('React');
function App() {
return (
// LoadingGlimmer is rendered via the Suspense fallback
<Suspense fallback={<LoadingGlimmer />}>
<MainContent /> {/* MainContent may suspend */}
</Suspense>
);
}
让我们来总结一下这里发生的事情。
- 如果
MainContent
挂起是因为它正在等待一些异步资源(例如数据),那么包装MainContent
的Suspense
组件将检测到它挂起了,并将渲染fallback
元素(在本例中为LoadingGlimmer
),直到MainContent
准备好渲染。请注意,这也间接包括MainContent
的后代,这些后代也可能挂起。
Suspense 的好处是您可以对如何为组件树的不同部分累积加载状态进行精细控制。
/**
* App.react.js
*/
const React = require('React');
const {Suspense} = require('React');
function App() {
return (
// A LoadingGlimmer for all content is rendered via the Suspense fallback
<Suspense fallback={<LoadingGlimmer />}>
<MainContent />
<SecondaryContent /> {/* SecondaryContent can also suspend */}
</Suspense>
);
}
- 在本例中,
MainContent
和SecondaryContent
可能会在加载其异步资源时挂起;通过将两者都包装在Suspense
中,我们可以显示一个加载状态,直到它们都“就绪”,然后在所有内容都成功加载后,以一次绘制操作渲染整个内容。 - 事实上,
MainContent
和SecondaryContent
可能会因除获取数据之外的其他原因而挂起,但同一个Suspense
组件可用于渲染回退,直到子树中所有组件都准备好渲染。请注意,这也间接包括MainContent
或SecondaryContent
的后代,这些后代也可能挂起。
相反,您也可以决定对加载 UI 进行更精细的控制,并将 Suspense 组件包装在组件树的较小部分或单个部分周围。
/**
* App.react.js
*/
const React = require('React');
const {Suspense} = require('React');
function App() {
return (
<>
{/* Show a separate loading UI for the LeftHandColumn */}
<Suspense fallback={<LeftColumnPlaceholder />}>
<LeftColumn />
</Suspense>
{/* Show a separate loading UI for both the Main and Secondary content */}
<Suspense fallback={<LoadingGlimmer />}>
<MainContent />
<SecondaryContent />
</Suspense>
</>
);
}
- 在本例中,我们显示了 2 个单独的加载 UI。
- 一个在
LeftColumn
就绪之前显示。 - 另一个在
MainContent
和SecondaryContent
都就绪之前显示。
- 一个在
- 强大之处在于,通过将 Suspense 更精细地包装在我们的组件周围,我们允许其他组件在就绪后尽早渲染。在我们的示例中,通过将
MainContent
和SecondaryContent
分别包装在Suspense
下,我们允许LeftColumn
在其就绪后尽快渲染,这可能早于内容部分就绪。
挂起的过渡和更新
Suspense
边界回退使我们能够在最初渲染一些内容时描述加载占位符,但我们的应用程序还将具有不同内容之间的过渡。具体来说,当在已挂载的边界中切换两个组件时,您正在切换到的新组件可能尚未加载其所有异步依赖项,这意味着它也可能挂起。
在这种情况下,我们仍然会显示 Suspense
边界回退。但是,这意味着我们将隐藏现有内容,以支持显示 Suspense
回退。在未来版本的 React 中,当支持并发渲染时,React 将提供一个选项来支持这种情况,并在挂起时避免隐藏已经渲染的内容并显示 Suspense 回退。
我们在 Relay 中如何使用 Suspense
查询
在我们的案例中,查询组件是可能挂起的组件,因此我们使用 Suspense 在获取查询时渲染加载状态。让我们看看实际情况。
假设我们有以下查询渲染器组件。
/**
* MainContent.react.js
*
* Query Component
*/
const React = require('React');
const {graphql, usePreloadedQuery} = require('react-relay');
function MainContent(props) {
// Fetch and render a query
const data = usePreloadedQuery(
graphql`...`,
props.queryRef,
);
return (...);
}
/**
* App.react.js
*/
const React = require('React');
const {Suspense} = require('React');
function App() {
return (
// LoadingGlimmer is rendered via the Suspense fallback
<Suspense fallback={<LoadingGlimmer />}>
<MainContent /> {/* MainContent may suspend */}
</Suspense>
);
}
让我们来总结一下这里发生的事情。
- 我们有一个
MainContent
组件,它是一个查询渲染器,用于获取和渲染查询。MainContent
在尝试获取查询时将挂起渲染,这表明它还没有准备好渲染,并且只有在查询被获取后才会解析。 - 包装
MainContent
的Suspense
组件将检测到MainContent
已挂起,并将渲染fallback
元素(在本例中为LoadingGlimmer
),直到MainContent
准备好渲染;也就是说,直到查询被获取。
片段
片段也与 Suspense 集成,以支持渲染正在 @defer'
的数据或 Relay Store 中部分可用的数据(即 部分渲染)。
过渡
此外,我们的重新获取 API(刷新和重新获取)以及 渲染连接 也与 Suspense 集成;对于这些用例,这些 API 也会挂起。
此页面有用吗?
通过以下操作,帮助我们让网站变得更好 回答几个简短的问题.