在 React 通过 Suspense 实现以同步的方式获取数据
同步效果如下
建议大家阅读完之后再回来体验
什么是以同步的方式来获取数据
同步的方式(代码书写层面)来获取数据,其实也是践行代数效应(有兴趣的可以看看我的 践行代数效应的 hook 实现)的一种,在这种情况下,我们不必关心数据是怎么来的(远程获取或者本地获取),我们只需要调用获取数据的函数,然后根据返回值来使用就行了。
举个例子,正常在 React 中都是异步来获取数据,比如下面这样
function App() {
const [state,setState] = useState(null);
useEffect(()=>{
getData().then(res =>{
setState(res);
})
},[])
return <div> {state} </div>
}
如果是同步来获取的话就是下面这个样
function App() {
const state = getData();
return <div> {state} </div>
}
是不是感觉代码简单了很多。当然现在这样写在正常的 js 中肯定是行不通的,但是未来还是很有可能实现的。虽然在 js 中不行,但是在 React 中还是可以落地的。
下面我们先过一下 React 文档:
React 官方文档
React.Suspense
可以指定加载指示器(loading indicator),以防其组件树中的某些子组件尚未具备渲染条件。在未来,我们计划让Suspense
处理更多的场景,如数据获取等。你可以在 我们的路线图 了解这一点。
只有启用了 Suspense 的数据源才会激活 Suspense 组件,它们包括:
Suspense 无法 检测在 Effect 或事件处理程序中获取数据的情况。
在上面的
Albums
组件中,正确的数据加载方法取决于你使用的框架。如果你使用了支持 Suspense 的框架,你会在其数据获取文档中找到详细信息。目前还不支持脱离框架使用支持 Suspense 的数据获取。实现支持 Suspense 的数据源的要求是不稳定的,也没有文档。用于将数据源与 Suspense 集成的官方 API 将在未来的 React 版本中发布。
文档总结
React 中的 Suspense 在未来会处理请求数据(也就能达到我们前面说的同步的效果),处理的数据源需要 Suspense 认识,React 目前还不支持(因为实现支持数据源的要求还没确定)也没有文档,但是已经有框架支持这么用——Relay 和 Next.js。
但是我有点懒,暂时不想去翻这俩框架,还是自己整一个吧
如何实现
关键点是要让 Suspense 认识,那我也不知道怎么才能让他认识,不过 Suspense 认识 Lazy 啊,那我们来借鉴下 Lazy 的实现不就行了。
实现看起来不难,实际上也不难。不过只有这个也不够,还有个问题, lazy 是把请求的 Promise 存储在 fiber 上的,那我们直接在函数组件里面用,函数组件每次都是重新执行啊,那我们要把 Promise 存在哪来保证每次拿到的都一样呢?
想到了几种方案:
- 利用闭包
- 采用类实现(使用起来不够优雅)
- 直接通过变量来实现(太原始了)
最终选用了通过闭包来实现
const Uninitialized = -1;
const Pending = 0;
const Resolved = 1;
const Rejected = 2;
function syncRequest(api) {
const payload = {
_result: api,
_status: Uninitialized
};
return function (query) {
if (payload._status === Uninitialized) {
const ctor = payload._result;
const thenable = ctor(query);
thenable.then(
moduleObject => {
if (payload._status === Pending || payload._status === Uninitialized) {
const resolved = payload;
resolved._status = Resolved;
resolved._result = moduleObject;
}
},
error => {
if (payload._status === Pending || payload._status === Uninitialized) {
const rejected = payload;
rejected._status = Rejected;
rejected._result = error;
}
},
);
if (payload._status === Uninitialized) {
const pending = payload;
pending._status = Pending;
pending._result = thenable;
}
}
if (payload._status === Resolved) {
const data = payload._result;
return data;
} else {
throw payload._result;
}
};
}
// 使用
function getData(data) {
return new Promise(function(resolve, reject) {
setTimeout(()=>{
resolve(data)
},3000);
})
}
const syncGetData = syncRequest(getData);
function Fn(props) {
const data = syncGetData(123);
return <div className="App">
<p>{data}</p>
</div>
}
function App() {
return (
<Suspense fallback={ <div>加载中</div> }>
<Fn />
</Suspense>
)
}
这样虽然实现了同步请求的功能,但是还是有点小问题,那就是不能更新数据。 所以呢核心需求就是需要更新,更新的花就只能通过 React 提供的东西来更新了,所以我们使用 自定义hook 来重新优化一下。
import { useState } from "react";
const Uninitialized = -1;
const Pending = 0;
const Resolved = 1;
const Rejected = 2;
function syncRequest(api) {
const payload = {
_result: api,
_status: Uninitialized,
_initApi: api,
_query: undefined,
};
function read(query) {
const [_, forceUpdate] = useState(1);
payload.forceUpdate = forceUpdate;
// query 是请求参数
const queryData = payload._query || query;
if (payload._status === Uninitialized) {
const ctor = payload._result;
const thenable = ctor(queryData);
thenable.then(
moduleObject => {
if (payload._status === Pending || payload._status === Uninitialized) {
const resolved = payload;
resolved._status = Resolved;
resolved._result = moduleObject;
}
},
error => {
if (payload._status === Pending || payload._status === Uninitialized) {
const rejected = payload;
rejected._status = Rejected;
rejected._result = error;
}
},
);
if (payload._status === Uninitialized) {
const pending = payload;
pending._status = Pending;
pending._result = thenable;
}
}
if (payload._status === Resolved) {
const data = payload._result;
return data;
} else {
throw payload._result;
}
};
function update(query) {
// query 是新的请求参数
// 重置内部变量
payload._result = payload._initApi;
payload._status = Uninitialized;
payload._query = query;
payload.forceUpdate(num=>num 1);
}
return {
read,
update
}
}
// 使用
function getData(data) {
return new Promise(function(resolve, reject) {
setTimeout(()=>{
resolve(data)
},3000);
})
}
const syncGetData = syncRequest(getData);
function Fn() {
const data = syncGetData.read(124);
const upate = () => {
syncGetData.update(data 1)
}
return <div className="App" onClick={upate}>
更新{data}
</div>
}
function App() {
return (
<Suspense fallback={ <div>加载中</div> }>
<Fn />
</Suspense>
)
}
最后
参考资料
React18.2.0源码
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhfhbjfc
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01 -
怎样阻止微信小程序自动打开
PHP中文网 06-13