react-query手把手教程18-Suspense

601 阅读3分钟

该系列其他文章可以点击查看专栏👉🏻👉🏻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)包裹了一个有两个查询请求(userQuerygistsQuery)的子组件(UserAndGists)。

你可以在react-query devtools中观察这两个查询请求,此时会发现是["user", userId]查询先请求,等该请求结束后["gists", userId]才会继续请求,直到两个请求串行请求完毕,最终才会展示数据。

!!!请注意!!!

使用{suspense: true}配置的userQuery,将会以串行的方式进行请求,必须等到所有串行请求结束,才会加载最终的组件。但是大多数情况下,我们希望的是并行请求。因此推荐的方案是使用之前例子中的模式,将组件拆分成两个,请求就又会变成并行的了。

因此请一定要注意:新手在这里容易忽略串行请求,导致一些问题

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 26 天,点击查看活动详情