Await

Await组件是用来搭配 React 中Suspense组件使用的异步处理组件。其主要功能是用来在异步任务处理过程中,保证页面展示使用的。

默认情况下,所有的路由都是立刻加载完毕的,但是如果路由使用了耗时比较长的loader方法,那就会在等待数据加载的时候,始终保持空白的页面展示,这对于保持良好的用户体验是十分不利的。React Router 借助 React 中提供的Suspense组件,定义了Await组件用于根据异步方法执行的结果,动态的决定页面中实际组件的渲染。

React 提供的Suspense组件通过fallback属性可以在其内部的组件不能渲染的时候,先渲染一个临时的组件作为代替。这样就可以给内部的Await组件一个加载界面。这样Await组件就可以重点关注loader方法中异步过程执行成功或者执行失败的处理了。

Await组件在 React Router 中的定义如下。

declare function Await(props: AwaitProps): React.ReactElement;

interface AwaitProps {
  children: RecordingState.ReactNode | AwaitResolveRenderFunction;
  errorElement?: RecordingState.ReactNode;
  resolve: TrackedPromise | any;
}

interface AwaitResolveRenderFunction {
  (data: Awaited<any>): React.ReactElement;
}

Await组件中的children属性既可以渲染一个 React 组件,也可以执行一个异步过程,并将异步过程的执行结果作为渲染内容。例如以下两个示例。

// 使用一个函数作为Await组件的children。
<Await resolve={loadingPromise}>
  {(resolvedValue) => <SomeView items={resolvedValue} />}
</Await>;

// 使用一个组件作为Await组件的children
function ResolvedView() {
  const resolvedValue = useAsyncValue();

  return <div></div>;
}

<Await resolve={loadingPromise}>
  <ResolvedView />
</Await>;

resolve属性

Await组件中的resolve属性看起来并不容易理解,实际上这个属性是用来从loader方法中获取需要提取到Await组件的children中的异步处理结果的。简而言之就是路由组件的loader方法会返回一个使用defer()函数组装的异步结果,之后路由组件可以使用useLoadData()获取这个使用defer()组装的异步结果内容,位于路由组件中的Await组件就可以使用resolve属性指定异步结果中的部分内容,用以传递给Await组件中的children组件。

以下是一个可以清楚展现这个处理过程的示例。

<Route
  loader={async () => {
    let users = await getUsers();
    let privileges = getPrivileges();

    return defer({
      users,
      privileges,
    });
  }}
  element={<Users />}
/>;

function Users() {
  const { users, privileges } = useLoadData();

  return (
    <div>
      <h1>Users</h1>
      <React.Suspense fallback={<LoadingSkeleton />}>
        <Await resolve={privileges}>
          <Privileges />
        </Await>
      </React.Suspense>
    </div>
  );
}

function Privileges() {
  const privilges = useAsyncValue();

  return <div>{/* 处理实际的输出 */}</div>;
}

在这个示例中出现了defer()useAsyncValue()两个函数和 Hook,这两个内容在Await组件的正常工作中都是十分重要的。

defer

defer()函数是 React Router 提供的用在loader方法中组装异步结果使用的。defer()函数可以在loader方法中将异步执行结果和同步执行结果组装成一个返回值对象。这种操作比较像Promise.all(),但是Promise.all()需要内部所有内容都是Promise,但是defer()中组装的内容是可以混合的。

useAsyncValue

useAsyncValue()这个 Hook 的功能就是从组件父级Await组件中获取异步结果。它所获得的内容就是Awaitresolve属性所指定的内容。

errorElement属性

errorElement属性接受的是当异步出错时候需要渲染的组件,它实际上可以理解为异步执行出错时的children

useAsyncError

useAsyncValue()功能近似,useAsyncError() Hook 是用来在errorElement组件中获取异步处理过程中抛出的错误的。