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
经验文章,请点击苏南大叔的博客文章:
本博客不欢迎:各种镜像采集行为。请尊重原创文章内容,转载请保留作者链接。