该系列其他文章可以点击查看专栏👉🏻👉🏻react-query手把手教程系列
场景
Suspense是react中可以用于协调异步操作的加载方法,通常用于使用React.lazy加载的组件,或者是需要获取数据的相关组件中。
在react-query中,可以通过对useQuery添加一个配置项来支持Suspense。
实践
Suspense
下面的例子中,该页面会展示两个组件,期望的用户体验是如果两个查询都没有加载完前,不显示任何一个组件,展示loading动画。
基于之前的学习内容,你可以非常快的写出下面的伪代码:
const App = ({userId}) => {
const userQuery = useQuery(
["user", userId],
() => fetchUser(userId)
);
const gistsQuery = useQuery(
["gists", userId],
() => fetchGists(userId)
);
if (userQuery.isLoading || gistsQuery.isLoading) {
return <LoadingSpinner />;
}
return (
<div>
<User user={userQuery.data} />
<Gists gists={gistsQuery.data} />
</div>
);
}
上面的代码中,我们将加载状态的管理提升到了父级,并且在请求完成后,将实际的数据传递给子组件。但是这并不是一个理想的状态,这里只希望父组件管理loading动画,其余的事情,在组件内部完成。不然一旦子组件增多,会导致父组件的代码越来越多难以管理。
为了完成上面的目标,可以使用react提供的Suspense,伪代码如下:
import {Suspense} from "react";
const App = ({userId}) => {
return <Suspense fallback={<LoadingSpinner />}>
<User userId={userId} />
<Gists userId={userId} />
</Suspense>
}
const User = ({userId}) => {
const userQuery = useQuery(
["user", userId],
() => fetchUser(userId),
{suspense: true}
);
return <div>{userQuery.data.name}</div>;
}
const Gists = ({userId}) => {
const gistsQuery = useQuery(
["gists", userId],
() => fetchGists(userId),
{suspense: true}
);
return <div>{gistsQuery.data.length} Gists</div>;
}
在App组件内,将需要加载数据的子组件包裹在<Suspense>中,并且传入loading动画。在子组件内的查询中,只需要向useQuery的配置项中传入{suspense: true}即可。
当useQuery加载数据时,将会向react发送一个信号来挂起组件,挂起组件后将会回调loading动画展示,直到数据加载完成才会展示正常的界面。
请注意上面的示例代码中,并没有使用
isLoading属性来判断是否需要展示loading动画。而是采用Suspense的特性来加载loading动画,降低了代码的复杂程度。但是
Suspense并不会处理任何错误,因此需要配合错误边界一起使用👉🏻点击查看错误边界官方文档。react-query会在收到{suspense: true}的配置项后,默认打开useErrorEdge选项。
并行查询
假设上面的例子中,两个查询都在同一个组件内,并且它的父组件使用Suspense包裹该组件,此时的伪代码如下
import {Suspense} from "react";
const App = ({userId}) => {
return <Suspense fallback={<LoadingSpinner />}>
<UserAndGists userId={userId} />
</Suspense>
}
const UserAndGists = ({userId}) => {
const userQuery = useQuery(
["user", userId],
() => fetchUser(userId),
{suspense: true}
);
const gistsQuery = useQuery(
["gists", userId],
() => fetchGists(userId),
{suspense: true}
);
return (
<div>
{/* 这里是渲染数据的一些代码 ... */}
</div>
);
}
父组件(App)包裹了一个有两个查询请求(userQuery及gistsQuery)的子组件(UserAndGists)。
你可以在react-query devtools中观察这两个查询请求,此时会发现是["user", userId]查询先请求,等该请求结束后["gists", userId]才会继续请求,直到两个请求串行请求完毕,最终才会展示数据。
!!!请注意!!!使用
{suspense: true}配置的userQuery,将会以串行的方式进行请求,必须等到所有串行请求结束,才会加载最终的组件。但是大多数情况下,我们希望的是并行请求。因此推荐的方案是使用之前例子中的模式,将组件拆分成两个,请求就又会变成并行的了。因此请一定要注意:
新手在这里容易忽略串行请求,导致一些问题
开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 26 天,点击查看活动详情