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
组件中获取异步结果。它所获得的内容就是Await
的resolve
属性所指定的内容。
errorElement
属性
errorElement
属性接受的是当异步出错时候需要渲染的组件,它实际上可以理解为异步执行出错时的children
。
useAsyncError
跟useAsyncValue()
功能近似,useAsyncError()
Hook 是用来在errorElement
组件中获取异步处理过程中抛出的错误的。