渲染部分缓存数据
在 Relay 中渲染缓存数据时,可以执行部分渲染。我们定义“部分渲染” 为能够立即渲染部分缓存的查询的能力。也就是说,查询的一部分可能缺失,但查询的一部分可能已经被缓存。在这种情况下,我们希望能够立即渲染缓存的查询部分,而不必等待完整的查询被获取。
这在我们需要尽快渲染屏幕或页面,并且知道该页面的一些数据已经缓存,因此可以跳过加载状态的情况下非常有用。例如,以个人资料页面为例:用户姓名很可能在应用程序使用期间的某个时间点被缓存,因此在访问个人资料页面时,如果用户的姓名被缓存,我们希望立即渲染,即使个人资料页面的其余数据尚未可用。
片段作为部分渲染的边界
为此,我们依赖于片段组件挂起的能力(参见使用 Suspense 的加载状态部分)。如果片段组件在渲染期间声明的任何本地数据缺失,并且当前正在获取,则该组件将挂起。具体来说,它将挂起,直到它需要的数据被获取,即直到它所属的查询(其父查询)被获取。
让我们用一个例子解释一下这意味着什么。假设我们有以下片段组件
/**
* UsernameComponent.react.js
*
* Fragment Component
*/
import type {UsernameComponent_user$key} from 'UsernameComponent_user.graphql';
const React = require('React');
const {graphql, useFragment} = require('react-relay');
type Props = {
user: UsernameComponent_user$key,
};
function UsernameComponent(props: Props) {
const user = useFragment(
graphql`
fragment UsernameComponent_user on User {
username
}
`,
props.user,
);
return (...);
}
module.exports = UsernameComponent;
并且我们有以下查询组件,它查询一些数据,并且还包含上面的片段
/**
* AppTabs.react.js
*
* Query Loader Component
*/
// ....
const onSelectHomeTab = () => {
loadHomeTabQuery({id: '4'}, {fetchPolicy: 'store-or-network'});
}
// ...
/**
* HomeTab.react.js
*
* Query Component
*/
const React = require('React');
const {graphql, usePreloadedQuery} = require('react-relay');
const UsernameComponent = require('./UsernameComponent.react');
function HomeTab(props: Props) {
const data = usePreloadedQuery(
graphql`
query HomeTabQuery($id: ID!) {
user(id: $id) {
name
...UsernameComponent_user
}
}
`,
props.queryRef,
);
return (
<>
<h1>{data.user?.name}</h1>
<UsernameComponent user={data.user} />
</>
);
}
假设当此 HomeTab
组件被渲染时,我们已经以前获取了(仅) 具有 {id: 4}
的 User
的 name
,并且它被本地缓存到与我们当前 Relay 环境关联的 Relay 存储中。
如果我们尝试使用允许重用本地缓存数据的 fetchPolicy
('store-or-network'
或 'store-and-network'
)来渲染查询,则会发生以下情况
- 该查询将检查其所有本地必需数据是否缺失。在这种情况下,它没有缺失。具体来说,该查询只直接选择了
name
字段,并且该字段存在于存储中。- Relay 仅在本地声明且缺失时才认为数据缺失。换句话说,在片段扩展中选择的 data 不会影响外部查询或片段是否被确定为具有缺失数据。
- 鉴于查询没有任何数据缺失,它将被渲染,然后尝试渲染子
UsernameComponent
。 - 当
UsernameComponent
尝试渲染UsernameComponent_user
片段时,Relay 会注意到渲染所需的一些数据缺失;具体来说,username
缺失。此时,由于UsernameComponent
具有缺失数据,它将挂起渲染,直到网络请求完成。请注意,无论你选择哪个fetchPolicy
,如果完整查询(即包括片段)的任何数据缺失,网络请求始终会启动。
此时,当 UsernameComponent
由于缺少 username
而挂起时,理想情况下我们应该能够立即渲染 User
的 name
,因为它被本地缓存。但是,由于我们没有使用 Suspense
组件来捕获片段的挂起,因此挂起将冒泡并导致整个 App
组件挂起。
为了实现所需的效果,即在 username
缺失的情况下,当 name
可用时渲染它,我们只需要将 UsernameComponent
包裹在 Suspense
中,以允许 App
的其他部分继续渲染
/**
* HomeTab.react.js
*
* Query Component
*/
const React = require('React');
const {Suspense} = require('React');
const {graphql, usePreloadedQuery} = require('react-relay');
const UsernameComponent = require('./UsernameComponent.react');
function HomeTab() {
const data = usePreloadedQuery(
graphql`
query AppQuery($id: ID!) {
user(id: $id) {
name
...UsernameComponent_user
}
}
`,
props.queryRef,
);
return (
<>
<h1>{data.user?.name}</h1>
{/*
Wrap the UserComponent in Suspense to allow other parts of the
App to be rendered even if the username is missing.
*/}
<Suspense fallback={<LoadingSpinner label="Fetching username" />}>
<UsernameComponent user={data.user} />
</Suspense>
</>
);
}
我们上面描述的过程对嵌套片段(即包含在其他片段中的片段)也适用。这意味着,如果渲染片段所需的数据被本地缓存,则片段组件将能够渲染,无论其任何子片段或后代片段的数据是否缺失。如果子片段的数据缺失,我们可以将其包裹在 Suspense
组件中,以允许其他片段和应用程序的其他部分继续渲染。
正如我们在激励示例中提到的,这是可取的,因为它可以让我们完全跳过加载状态。更具体地说,能够渲染部分可用的数据使我们能够渲染更接近最终渲染状态的中间 UI 状态。
此页面是否有用?
通过以下方式帮助我们使网站更完善 回答几个简单问题.