[译] 实战 React 18 中的 Suspense

news2024/11/20 2:30:31

1a4368cdf8c1a969944ba8f04ba420cd.jpeg

> 原文:https://dev.to/darkmavis1980/a-practical-example-of-suspense-in-react-18-3lln

React 18 带来了很多变化,它不会破坏你已经编写过的代码,并且有很多改进和一些新概念。

它也让很多开发人员,包括我,意识到我们错误地使用了useEffect hook。但话说回来,我们被其名称所误导了,因为实际上useEffect并不应该被用于副作用。

在 React 18 中,虽然仍然可以使用useEffect来完成一些事情,如使用 API 接口读取的数据填充状态,但实际上不应该将其用于此类目的。如果你在应用程序中启用StrictMode,在开发模式下,你将发现使用useEffect会被调用两次,因为现在React会mount 组件、卸载它,然后再次 mount 它,以检查代码是否运行正常。

Suspense 来了

我们应该用来取而代之的,是新的Suspense组件(虽然它已经存在于 React 17 中,但现在是推荐的方法),此组件将会按照以下方式工作:

<Suspense fallback={<p>Loading...</p>}>
  <MyComponent />
</Suspense>

上面的代码将会包裹一个组件,这个组件从某些数据源中加载数据,并在完成数据获取之前显示fallback。

Suspense 是什么

简而言之,可能和你想的不同,Suspense 并不是一个新的用于获取数据的接口,因为该工作仍然由诸如“fetch”或“axios”等库委派执行,而它实际上允许你将这些库与 React 集成,并且它的真正工作只是“在加载时显示这段代码,而在完成后显示那段代码”,仅此而已。

Suspense 如何工作

首先,你需要了解 Promise 的工作原理以及它的状态。无论使用传统方式new Promise()还是新的async/await语法来使用promise,在任何情况下,promise始终具有以下这三种状态:

  • pending -> 它仍在处理请求

  • resolved -> 请求已返回某些数据,我们获得了200 OK状态

  • rejected -> 出现了错误,获得了一个错误

Suspense使用的逻辑与ErrorBoundary完全相反,因此如果代码引发异常(因为它仍处于加载状态或者由于加载失败),则显示fallback;如果成功解析,则显示子组件。

举个例子

来看一个简单的例子,我们只需创建一个组件来获取API中的某些数据,并且希望在准备好后渲染该组件。

注意
为了简化,这里不会提到如何使用“startTransition”,添加错误边界,甚至不会涉及各种策略之间的区别,例如“fetch-on-render”、“fetch-then-render”等等...

包装 fetch 逻辑

如上所述,当我们的组件正在加载数据或失败时,需要抛出异常,但是一旦成功解决了Promise,就可以简单地返回响应。

为此,我们需要使用以下函数包装我们的请求:

// wrapPromise.js
/**
 * 将promise包装,以便可以与React Suspense一起使用
 * @param {Promise} 要处理的promise
 * @returns {Object} 与Suspense兼容的响应对象
 */
function wrapPromise(promise) {
  let status = 'pending';
  let response;

  const suspender = promise.then(
    res => {
      status = 'success';
      response = res;
    },
    err => {
      status = 'error';
      response = err;
    },
  );

  const handler = {
    pending: () => {
      throw suspender;
    },
    error: () => {
      throw response;
    },
    default: () => response,
  };

  const read = () => {
    const result = handler[status] ? handler[status]() : handler.default();
    return result;
  };

  return { read };
}

export default wrapPromise;

因此,上面的代码将检查我们Promise的状态,然后返回一个名为“read”的函数,稍后我们将在组件中调用它。

现在,我们需要使用它包装接口请求库(例子中是axios),创建一个非常简单的函数:

//fetchData.js
import axios from 'axios';
import wrapPromise from './wrapPromise';

/**
* 用wrapPromise函数包装Axios请求
* @param {string} 要获取的URL
* @returns {Promise} 包装的promise
*/
function fetchData(url) {
     const promise = axios.get(url).then(({data}) => data);

     return wrapPromise(promise);
}

export default fetchData;

这只是以接口请求库表现的一种抽象,我想强调这只是一种非常简单的实现,您可以将上面的所有代码扩展到任何需要做的工作中。在这里我使用了axios,但你可以根据自己的需要使用任何东西。

在组件中读取数据

当获取方面的所有内容都准备好后,我们来在组件中使用它。假设有一个简单的组件,只需从某个接口读取名称列表并打印。不同于习惯中在组件中通过useEffect钩子调用 fetch 的做法,这一次我们要直接在组件开始时(放在任何 hooks 之外),使用我们在包装器中导出的read方法来调用请求,因此我们的Names组件大概是这个样子的:

// names.jsx
import React from 'react';
import fetchData from '../../api/fetchData.js';

const resource = fetchData('/sample.json');
const Names = () => {
  const namesList = resource.read();

  // rest of the code
}

这里所做的是,当调用组件时,read()函数将开始抛出异常,直到完全解析完成;其后,会继续执行其余代码,在此例中也就是继续 render。所以该组件的全部代码如下:

// names.jsx
import React from 'react';
import fetchData from '../../api/fetchData.js';

const resource = fetchData('/sample.json');

const Names = () => {
  const namesList = resource.read();

  return (
    <div>
      <h2>List of names</h2>
      <ul>
        {namesList.map(item => (
          <li key={item.id}>
            {item.name}
          </li>))}
      </ul>
    </div>
  );
};

export default Names;

父组件

现在 Suspense 将要发挥作用了,首先需要在父组件中导入它:

// parent.jsx
import React, { Suspense } from 'react';
import Names from './names';

const Home = () => (
  <div>
    <Suspense fallback={<p>Loading...</p>}>
      <Names />
    </Suspense>
  </div>
);

export default Home;

到底肿么了?
我们将Suspense作为React组件导入,然后使用它来包装获取数据的组件,在这些数据被 resolve 之前,它将只会渲染“fallback”组件,因此只是<p>Loading...</p>或其他什么你需要的自定义组件。

结论

长时间使用useEffect以实现相同的结果后,当我第一次看到 Suspanse 这种用法时,我对这种新方法有些怀疑。包装获取库的整个过程有点让人生疑。但是现在,我可以看到它的好处,它非常容易处理加载状态,它抽象掉了一些代码,使其易于重用,并通过消除(好吧,至少在大多数情况下)组件本身的“useEffect”钩子简化了组件的代码,这在以前可是个让人头疼的事情。

2faf6605a298a66f6dccc774716f2b21.png59479d008c64482f47556dd3f2924ee9.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/454774.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue---mixin混入

一个混入对象可以包含任意组件选项&#xff08;如data、methods、created、mounted等等&#xff09;。当组件使用混入对象时&#xff0c;所有混入对象的选项将被“混合”进入该组件本身的选项。 我们可以使用混入&#xff0c;向组件注入自定义的行为。 和组件注册和指令一样 vu…

Oracle Linux 9 上基于 CRI-O 安装 Kubernetes 1.27 集群

Oracle Linux 9 上基于 CRI-O 安装 Kubernetes 1.27 集群 1. 禁用 swap2. 禁用防火墙3. 将 SELinux 设置为 permissive 模式4. 安装cri-o5. 安装kubelet kubeadm kubectl6. 更新模块设置7. 初始化Kubernetes集群8. 配置集群访问9. 安装网络插件10. 验证集群 1. 禁用 swap sudo…

docker容器原样迁移完整过程(nignx例子)

我们在测试服务器上&#xff0c;辛辛苦苦开发&#xff0c;各种配置好了服务&#xff0c;然后想着傻瓜式的迁移部署。接下来的就是干货了 过程描述&#xff1a; 为了体现一个完成性的描述&#xff0c;我们最初拉镜像开始&#xff0c;一直说到迁移后的服务正常运行。 接下来以ng…

centos7 查看服务器配置信息

1.linux查看版本当前操作系统发行信息 cat /etc/centos-release cat /etc/centos-release 2、查看内核版本uname -a或者cat /proc/version 3、查看CPU参数 1&#xff09;、查看 CPU 物理个数   grep physical id /proc/cpuinfo | sort -u | wc -l 2&#xff09;、查看 CPU …

如何降低小程序开发费用:从项目管理到技术选型

小程序的开发费用是许多企业和初创公司的瓶颈。在本文中&#xff0c;我们将介绍如何通过项目管理和技术选型来降低小程序开发费用&#xff0c;让您的企业更加高效。我们会详细阐述如何在项目管理中制定清晰的项目计划、与开发团队密切合作、采用敏捷开发方法。在技术选型方面&a…

jmeter压测结果分析

jmeter结果查看主要在结果树和聚合报告&#xff0c;实际在做压测过程中不做可视化操作&#xff0c;用命令行执行&#xff0c;再查看测试报告。 python在本地起服务 cmd打开命令框执行语句&#xff1a;python -m http.server 9090&#xff08;端口号&#xff0c;可自定义&…

magento webapi 接口返回 json对象

前言 现在主流的项目开发都是前后端分离&#xff0c;数据通过json对象格式进行传输。但是magento框架&#xff0c;和传统PHP框架相比&#xff0c;区别很大。虽然也支持以RestApi的形式传输数据&#xff0c;但是要么格式并非是传统jsonObject要么就是需要大量的get、set方法。本…

TypeScript学习笔记以及学习中遇到的问题

本笔记是来自翻阅xcatliu的typeScript入门教程文档、TypeScript官方文档的部分摘录、以及观看B站学习视频进行笔记记录与知识点补充、本人实际使用时遇到的问题与解决记录、碎片化接触到相关知识点合并整理而成 仅供本人洪的学习使用 hello TypeScript 一、TypeScript安装 Ty…

【QT】如何检测目录或文件中的内容被修改,可以使用QFileSystemWatcher类进行检测

目录 1. QFileSystemWatcher类的介绍2. QFileSystemWatcher的公共函数2.1 构造函数2.2 析构函数2.3 添加监控的路径2.4 返回正在监控的目录或文件2.5 从文件系统监视程序中删除指定的路径 3. QFileSystemWatcher的信号4. 测试代码4.1 操作步骤4.2 MainWindow.h4.3 MainWindow.c…

Spring之Bean的配置与实例

Spring之Bean的配置与实例 一、Bean的基础配置1. Bean基础配置【重点】配置说明代码演示运行结果 2. Bean别名配置配置说明代码演示打印结果 3. Bean作用范围配置【重点】配置说明代码演示打印结果 二、Bean的实例化1. Bean是如何创建的2. 实例化Bean的三种方式2.1 构造方法方式…

数据库系统-数据库查询实现算法之

文章目录 一、一趟扫描算法1.1 算法概述1.2 算法逻辑&物理实现1.2.1 逻辑层面1.2.2 物理层面1.2.2.1 P11.2.2.2 P21.2.2.3 P31.2.2.4 P4 1.3 迭代器构造查询实现算法1.4 关系操作的一趟扫描算法1.4 基于索引的查询实现算法 二、两趟扫描算法2.1 两趟算法基本思想2.2 多路归…

SaaS是什么?企业为什么要有SaaS系统?

什么是SaaS系统&#xff1f;企业为什么要有SaaS系统&#xff1f; 近几年&#xff0c;SaaS突然变成了一个热门词汇&#xff0c;无论是一些权威报告&#xff0c;还是知乎上知友们热烈的讨论&#xff0c;对于Saas系统可谓是各有各的见解和看法。 今天就综合几位答主的观点&#…

【1163. 按字典序排在最后的子串】

来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 描述&#xff1a; 给你一个字符串 s &#xff0c;找出它的所有子串并按字典序排列&#xff0c;返回排在最后的那个子串。 示例 1&#xff1a; 输入&#xff1a;s "abab" 输出&#xff1a;"bab"…

安波福又有大动作,行泊一体中国方案即将量产

“安波福的目标是&#xff0c;2030年营收达到400亿美元规模&#xff0c;这其中有一大部分收入来自于软件。”安波福亚太区总裁杨晓明在接受高工智能汽车提问时表示。 他介绍&#xff0c;2030年软件业务目标是达到60亿美元的规模&#xff0c;而在2022年&#xff0c;安波福实现了…

Nacos身份绕过漏洞复现(QVD-2023-6271)

Nacos身份绕过漏洞复现&#xff08;QVD-2023-6271&#xff09; 公司上级预警QVD-2023-6271&#xff0c;领导安排进行排查。 本着知己知彼的原则&#xff0c;我在本地将该漏洞复现出来。 漏洞原理&#xff1a;Nacos 在默认配置下未对 token.secret.key 进行修改&#xff0c;导…

【好题】好题分享

1001-四舍五入_牛客竞赛语法入门班数组模拟、枚举、贪心习题 (nowcoder.com) 题目描述 四舍五入是个好东西。比如你只考了45分&#xff0c;四舍五入后你是50分再四舍五入你就是满分啦&#xff01;qdgg刚考完拓扑。成绩十分不理想。但老师觉得他每天都很认真的听课很不容易。于是…

ubuntu 22 安装

下载镜像 下载 https://ubuntu.com/download/server 我这里下载了 22 服务器版 使用工具制作U盘镜像 安装完毕后 插入安装的电脑U盘插口 DEL 进入 选择U盘启动 进入后 我这里选择了第一个&#xff0c;没有选择mini版本 之后进入后按推荐一直安装 在选择静态地址的时候 subn…

你还在用Object.equals()方法吗?

当《阿里巴巴Java开发手册》发布后&#xff0c;我也是仔细进行了阅读&#xff0c;想从中找出一些“标准”&#xff0c;让自己的代码质量提高。手册中对 Object 的 equals 方法的使用进行了强制&#xff0c;而且推荐使用 JDK7 中工具类 Objects 的 equals 方法&#xff0c;至此之…

我在 Linux部署皕杰报表遇到的问题及解决方法

Linux是一种自由和开放源码的类 UNIX 操作系统&#xff0c;作为服务器的操作系统广泛应用。Linux由林纳斯托瓦兹在赫尔辛基大学上学时创立&#xff0c;主要受到 Minix 和 Unix 思想的启发。Linux英文解释为 Linux is not Unix。 皕杰报表支持在linux系统中部署&#xff0c;只需…

短视频矩阵优化系统开发步骤:

1. 矩阵号注册&#xff1a;用户可以通过该功能注册矩阵号&#xff0c;填写个人基本信息并上传身份证明材料进行实名认证。 2. 矩阵号登录&#xff1a;用户使用注册成功的账号密码登录矩阵号系统。 3. 矩阵号管理&#xff1a;用户可以通过该功能管理自己的矩阵号&#xff0c;包…