React路由,Await如何加载defer延迟数据?配合Suspense
发布于 作者:苏南大叔 来源:程序如此灵动~
看清楚,本文的<Await>是大写开头,来自于ReactRouter,并不是大家所熟知的那个await关键字!虽然效果类似,但是<Await>是ReactRouterDom提供的一个高阶组件。而Suspense则来自于ReactDom。本文将讲述<Await>组件如何对接defer的远程数据,如何配合Suspense组件延迟显示数据。

苏南大叔的“程序如此灵动”博客,记录苏南大叔的代码编程经验总结。本文测试环境:nodejs@20.18.0,create-react-app@5.0.1,react-router-dom@6.27.0,react@18.3.1。
前文回顾
本文中的局部延迟加载的效果,使用的组件主要有两个:
- 来自
ReactRouterDom的Await。 - 来自
ReactDom的Suspense。
以前的文章里面,Suspense是配合Lazy完成组件的懒加载效果的,其触发条件是【需要的时候】再加载。参考文章:
本文里面,Suspense是配合Await完成组件的延迟加载效果的,其触发条件是【数据正常返回的时候】再显示。而数据接口的请求过程,一般都是个promise的过程。ReactRouter提供了一个defer,对其进行了包装。参考文章:
如果把defer请求远程数据的过程,包装进路由数组的.loader属性里面的话,就可以使用useLoaderData()获取这个包装过的DefereredDataPromise。参考文章:
三种访问远程数据方式
把这个loadDataPromise()移动到src/helper/LoadData.js文件里面。延展出三个变种:
loadDataPromise()。loadDataAwait(),这里的await指的就是await关键字。由于驼峰命名的缘故,变成了大写。loadDataDefer(),一定要看代码注释!!!这里有认知陷阱!!
/src/helper/LoadData.js:
import { defer } from "react-router-dom";
const loadDataPromise = () => {
return new Promise((r) => {
setTimeout(() => {
r({ name: "苏南大叔" });
}, 2000);
});
};
async function loadDataAwait() {
return await loadDataPromise();
}
function loadDataDefer() {
let _promise = loadDataPromise(); // 不能合并简写!!必须赋值到一个变量里面。
return defer({ _promise }); // 注意,这里的 _promise 是个key,在组件里面要再次导出的,名字必须一致...
}
export { loadDataPromise, loadDataAwait, loadDataDefer };方案一,promise 最简单
src/Nnn.js:
import { Suspense } from "react";
import { Await } from "react-router";
import { loadDataPromise } from "./helper/LoadData";
function Nnn() {
let _promise = loadDataPromise();
return (
<>
欢迎光临,
<Suspense fallback={<>loading</>}>
<Await resolve={_promise}>
{(data) => {
return <span>{data.name}</span>;
}}
</Await>
</ Suspense>
【刷新局部显示loading】
</>
);
}
export default Nnn;
方案二,defer 最新潮
后者代码如下:src/routes.js:
import { loadDataDefer } from "./helper/LoadData";
import Layout from "./Layout";
import Nnn from "./Nnn";
let routes = [
{
path: "/",
element: <Layout></Layout>,
children: [
{
path: "n",
element: <Nnn></Nnn>,
loader: () => {
return loadDataDefer();
},
},
],
},
];
export default routes;src/Nnn.js
import { Suspense } from "react";
import { Await, useLoaderData } from "react-router";
function Nnn() {
let { _promise } = useLoaderData(); // 注意这里的 _promise 命名来自于 loadDataDefer() 定义内部的导出
return (
<>
欢迎光临,
<Suspense fallback={"loading"}>
<Await resolve={_promise}>
{(data) => {
return <span>{data.name}</span>;
}}
</Await>
</ Suspense>
【刷新局部显示loading】
</>
);
}
export default Nnn;
fallback
Suspense的fallback属性,就是准备“加载中”信息的。不准备组件,准备个纯文字信息也是可以的。比如:
<Suspense fallback={"loading"}></ Suspense>resolve
这里Await高阶组件的resolve属性,传递的是个promise。它决定了这个局部loading效果显示多久。本文最大的变数,就来自这个promise。
<Await resolve={ _promise }></Await>promise
这个部分,主要就是准备请求数据的接口。最终得到一个Promise,传递给Await高阶组件。这里可以使用下面的函数,模拟这个过程,也可以真实的请求一个数据接口。参考文章:
根据:三种数据接口函数,两种代码放置方法,排列组合一下,共六种方案。其中只有两种方案符合【局部延迟加载】预期。分别是:
- 原生的
loadDataPromise(),那么直接传入<Await>的resolve属性。【方案一】 loadDataDefer()放入到路由数组的loader里面,组件里面使用useLoadData()访问导出的promise。【方案二】
题外话【lazy版延迟加载】
Suspense高阶组件的真相!暂停渲染!显示fallback的内容。直到内部包裹的类promise组件被resolve!仔细体会!
附上lazy的相关关键代码。
import { Suspense, lazy } from "react";
import { loadDataPromise } from "./helper/LoadData";
const LazyChild = lazy(
() =>
new Promise((resolve) => {
loadDataPromise().then(
(data)=>{
resolve({ default: () => <>{data.name}</> });
}
)
})
);
function Nnn() {
return (
<>
欢迎光临,
<Suspense fallback={<>加载中</>}>
<LazyChild />
</ Suspense>
【刷新局部显示loading】
</>
);
}
export default Nnn;结语
其它的组合里面,要不就是语法错误,要不就是全局刷新而不是局部刷新,要不就是没有返回值。总之,不符合预期。Suspense也不是非要配合Await来加载数据,配合同组的lazy也是没有任何问题的。
更多苏南大叔的react经验文章,请点击苏南大叔的博客文章: