我们是袋鼠云数栈 UED 团队,致力于打造优秀的一站式数据中台产品。我们始终保持工匠精神,探索前端道路,为社区积累并传播经验价值。
本文作者:佳岚
Suspense
Suspense
组件我们并不陌生,中文名可以理解为暂停or悬停
, 在 React16 中我们通常在路由懒加载中配合 Lazy 组件一起使用 ,当然这也是官方早起版本推荐的唯一用法。
那它暂停了什么? 进行异步网络请求,然后再拿到请求后的数据进行渲染是很常见的需求,但这不可避免的需要先渲染一次没有数据的页面,数据返回后再去重新渲染。so , 我们想要暂停的就是第一次的无数据渲染。
通常我们在没有使用Suspense
时一般采用下面这种写法, 通过一个isLoading
状态来显示加载中或数据。这样代码是不会有任何问题,但我们需要手动去维护一个isLoading
状态的值。
const [data, isLoading] = fetchData("/api");
if (isLoading) {
return ;
}
r服务器托管网eturn ;
当我们使用Suspense
后,使用方法会变为如下, 我们只需将进行异步数据获取的组件进行包裹,并将加载中组件通过fallback
传入
return (
}>
);
那 React 是如何知道该显示MyComponent
还是Spinner
的?
答案就在于MyComponent
内部进行fetch
远程数据时做了一些手脚。
export const App = () => {
return (
}>
);
};
function Spining() {
return loading...
;
}
let data = null;
function MyComponent() {
if (!data) {
throw new Promise((resolve) => {
setTimeout(() => {
data = 'kunkun';
resolve(true);
}, 2000);
});
}
return (
My Component, data is {data}
);
}
Suspense
是根据捕获子组件内的异常来实现决定展示哪个组件的。这有点类似于ErrorBoundary
,不过ErrorBoundary
是捕获 Error 时就展示回退组件,而Suspense
捕获到的 Error 需要是一个Promise
对象(并非必须是 Promise 类型,thenable 的都可以)。
我们知道 Promise 有三个状态,pending
、fullfilled
、rejected
,当我们进行远程数据获取时,会创建一个Promise
,我们需要直接将这个Promise
作为Error
进行抛出,由 Suspense 进行捕获,捕获后对该thenable
对象的then
方法进行回调注册thenable.then(retry服务器托管网)
, 而 retry 方法就会开始一个调度任务进行更新,后面会详细讲。
知道了大致原理,这时还需要对我们的fetcher
进行一层包裹才能实际运用。
// MyComponent.tsx
const getList = wrapPromise(fetcher('http://api/getList'));
export function MyComponent() {
const data = getList.read();
return (
{data?.map((item) => (
- {item.name}
))}
);
}
function fetcher(url) {
return new Promise((resove, reject) => {
setTimeout(() => {
resove([{ name: 'This is Item1' }, { name: 'This is Item2' }]);
}, 1000);
});
}
// Promise包裹函数,用来满足Suspense的要求,在初始化时默认就会throw出去
function wrapPromise(promise) {
let status = 'pending';
let response;
const suspend = promise.then(
(res) => {
status = 'success';
response = res;
},
(err) => {
status = 'error';
response = err;
}
);
const read = () => {
switch (status) {
case 'pending':
throw suspend;
default:
return response;
}
};
return { read };
从上述代码我们可以注意到,通过const data = getList.read()
这种同步的方式我们就能拿到数据了。 注意: 上面这种写法并非一种范式,目前官方也没有给出推荐的写法
为了与Suspense
配合,则我们的请求可能会变得很不优雅
,官方推荐是直接让我们使用第三方框架提供的能力使用Suspense
请求数据,如 useSWR
等
下面时useSWR
的示例,简明了很多,并且对于Profile
组件,数据获取的写法可以看成是同步的了。
import { Suspense } from 'react'
import useSWR from 'swr'
function Profile () {
const { data } = useSWR('/api/user', fetcher, { suspense: true })
return hello, {data.name}
}
function App () {
return (
loading...
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
机房租用,北京机房租用,IDC机房托管, http://www.fwqtg.net