当前位置: 首页 > news >正文

为什么不要在循环,条件或嵌套函数中调用hooks

为什么不要在循环,条件或嵌套函数中调用hooks

  • 前言
  • useState Hook 的工作原理
  • 具体实现
    • 1、初始化
    • 2、第一次渲染
    • 3、后续渲染
    • 4、事件处理
    • 简单代码实现
  • 为什么顺序很重要
    • Bad Component 第一次渲染
    • Bad Component 第二次渲染
  • 总结

前言

自从 React 推出 hooks 的 API 后,相信大家对新 API 都很喜欢,但是它对你如何使用它会有一些奇怪的限制。比如,React 官网介绍了 Hooks 的这样一个限制:

不要在循环,条件或嵌套函数中调用 Hook, 确保总是在你的 React 函数的最顶层以及任何 return 之前调用他们。遵守这条规则,你就能确保 Hook 在每一次渲染中都按照同样的顺序被调用。这让 React 能够在多次的 useState 和 useEffect 调用之间保持 hook 状态的正确。

useState Hook 的工作原理

这个限制并不是 React 团队凭空造出来的,的确是由于 React Hooks 的实现设计而不得已为之。

为了让大家有一个更清晰的思维模型,我将用数组来模拟useState的简单实现。 首先让我们通过一个例子来看看 hook 是如何工作的。

我们首先从一个组件开始:

function RenderFunctionComponent() {const [firstName, setFirstName] = useState("Rudi");const [lastName, setLastName] = useState("Yardley");return (<Button onClick={() => setFirstName("Fred")}>Fred</Button>);
}

useState hook 背后的思想是,你可以使用 hook 函数返回的数组的第二个数组项作为 setter 函数,并且该 setter 将控制由 hook 管理的状态。

具体实现

1、初始化

创建两个空数组:settersstate

将 cursor 设置为 0
请添加图片描述

2、第一次渲染

首次运行组件函数。

每次useState()调用,在第一次运行时,都会将一个 setter 函数推送到 setters 数组上,然后将一些状态推送到 state 数组上。
请添加图片描述

3、后续渲染

每次后续渲染都会重置 cursor,并且仅从每个数组中读取这些值。
请添加图片描述

4、事件处理

每个 setter 都有对其 cursor 的引用,因此通过触发对 setter 的调用,setter 它将更改状态数组中该位置的状态值。
请添加图片描述

简单代码实现

下面通过一段简单的代码示例来演示该实现。

注意:这并不是 React 的底层实现,但对于我们理解 react hook 的心智模型非常有帮助。

const state = [];
const setters = [];
let cursor = 0;function createSetter(cursor) {return function setterWithCursor(newVal) {state[cursor] = newVal;};
}export function useState(initVal) {if (state[cursor] === undefined && setters[cursor] === undefined) {state.push(initVal);setters.push(createSetter(cursor));}const setter = setters[cursor];const value = state[cursor];cursor++;return [value, setter];
}function RenderFunctionComponent() {const [firstName, setFirstName] = useState('Rudi'); // cursor: 0const [lastName, setLastName] = useState('Yardley'); // cursor: 1return (<div><button onClick={() => setFirstName('Richard')}>Richard</button><button onClick={() => setLastName('Fred')}>Fred</button></div>);
}function MyComponent() {cursor = 0; // resetting the cursorreturn <RenderFunctionComponent />; // render
}console.log(state); // Pre-render: []
MyComponent();
console.log(state); // First-render: ['Rudi', 'Yardley']
MyComponent();
console.log(state); // Subsequent-render: ['Rudi', 'Yardley']// click the 'Richard' buttonconsole.log(state); // After-click: ['Richard', 'Yardley']

为什么顺序很重要

现在,如果我们根据某些外部因素甚至组件状态更改渲染周期的钩子顺序会发生什么?

让我们做 React 团队说你不应该做的事情:

let firstRender = true;function RenderFunctionComponent() {let initName;if(firstRender){[initName] = useState("Rudi");firstRender = false;}const [firstName, setFirstName] = useState(initName);const [lastName, setLastName] = useState("Yardley");return (<Button onClick={() => setFirstName("Fred")}>Fred</Button>);
} 

这里我们有useState的一个条件调用。让我们看看这对系统造成的破坏。

Bad Component 第一次渲染

请添加图片描述
我们的实例变量firstName和lastName包含正确的数据,但让我们看看第二次渲染会发生什么:

Bad Component 第二次渲染

请添加图片描述
现在,firstNamelastName发生了错位,我们的状态存储变得不一致了。这就是为什么保持正确顺序的重要性。

总结

通过对 useState 的简单实现来理解 react hooks 的幕后实现逻辑。考虑将状态作为一组数组存在的模型,那么我们不该违反其对应的使用规则。

相关文章:

为什么不要在循环,条件或嵌套函数中调用hooks

为什么不要在循环&#xff0c;条件或嵌套函数中调用hooks 前言useState Hook 的工作原理具体实现1、初始化2、第一次渲染3、后续渲染4、事件处理简单代码实现 为什么顺序很重要Bad Component 第一次渲染Bad Component 第二次渲染 总结 前言 自从 React 推出 hooks 的 API 后&a…...

将成功请求的数据 放入apipost接口测试工具,发送给后端后,部分符号丢失

将成功请求的数据 放入apipost接口测试工具&#xff0c;发送给后端后&#xff0c;部分符号丢失 apipost、接口测试、符号、丢失、错乱、变成空格背景 做CA对接&#xff0c;保存CA系统的校验数据&#xff0c;需要模仿前端请求调起接口&#xff0c;以便测试功能完整性。 问题描…...

N诺计算机考研-错题

B A.LLC,逻辑链路控制子层。一个主机中可能有多个进程在运行,它们可能同时与其他的一些进程(在同一主机或多个主机中)进行通信。因此在一个主机的 LLC子层的一个服务访问点,以便向多个进程提供服务。B.MAC地址,称为物理地址、硬件地址,也称为局域网地址,用来定义网络设…...

vue3 数字滚动组件封装

相关参考文献 干货满满!如何优雅简洁地实现时钟翻牌器(支持JS/Vue/React) Vue3 插件方式 安装插件: countup.js 封装组件: components/count-up/index.js <template><div class="countup-wrap"><slot name="prefix"></slot&g…...

如何确保消息只被消费一次:Java实现详解

引言 在分布式系统中&#xff0c;消息传递是系统组件间通信的重要方式&#xff0c;而确保消息在传递过程中只被消费一次是一个关键问题。如果一个消息被多次消费&#xff0c;可能会导致业务逻辑重复执行&#xff0c;进而产生数据不一致、错误操作等问题。特别是在金融、电商等…...

Web3技术在元宇宙中的应用:从区块链到智能合约

随着元宇宙的兴起&#xff0c;Web3技术正逐渐成为其基础&#xff0c;推动着数字空间的重塑。元宇宙不仅是一个虚拟世界&#xff0c;它还代表着一个由去中心化技术驱动的新生态系统。在这个系统中&#xff0c;区块链和智能合约发挥着至关重要的作用&#xff0c;为用户提供安全、…...

关于QSizeGrip在ui界面存在布局的情况下的不显示问题

直接重写resizeEvent你会发现&#xff1a;grip并没有显示 void XXXXX::resizeEvent(QResizeEvent *event) {QWidget::resizeEvent(event);this->m_sizeGrip->move(this->width() - this->m_sizeGrip->width() - 3,this->height() - this->m_sizeGrip->…...

开始场景的制作+气泡特效的添加

3D场景或2D场景的切换 1.新建项目时选择3D项目或2D项目 2.如下图操作&#xff1a; 开始前的固有流程 按照如下步骤进行操作&#xff0c;于步骤3中更改Company Name等属性&#xff1a; 本案例分辨率可以如下设置&#xff0c;有能力者可根据需要自行调整&#xff1a; 场景制作…...

位运算--(二进制中1的个数)

位运算是计算机科学中一种高效的操作方式&#xff0c;常用于处理二进制数据。在Java中&#xff0c;位运算通常通过位移操作符和位与操作符实现。 当然位运算还有一些其他的奇淫巧计&#xff0c;今天介绍两个常用的位运算方法&#xff1a;返回整数x的二进制第k位的值和返回x的最…...

使用Docker和Macvlan驱动程序模拟跨主机跨网段通信

以下是使用Docker和Macvlan驱动程序模拟跨主机跨网段通信的架构图&#xff1a; #mermaid-svg-b7wuGoTr6eQYSNHJ {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-b7wuGoTr6eQYSNHJ .error-icon{fill:#552222;}#mermai…...

RestCloud webservice 流程设计

RestCloud webservice 流程设计 操作步骤 离线数据集成&#xff08;首页&#xff09; → \rightarrow → 示例应用数据集成流程&#xff08;边栏&#xff09; → \rightarrow → 所有数据流程 → \rightarrow → webservice节点获取城市列表 → \rightarrow → 流程设计 …...

从入门到精通:QT 100个关键技术关键词

Qt基础概念 Qt Framework - 一个跨平台的C图形用户界面应用程序开发框架。它不仅提供了丰富的GUI组件&#xff0c;还包括网络、数据库访问、多媒体支持等功能。 Qt Creator - Qt官方提供的集成开发环境&#xff08;IDE&#xff09;&#xff0c;集成了代码编辑器、项目管理工具、…...

2024年双十一值得入手的好物有哪些?五大性价比拉满闭眼入好物盘点

随着2024年双十一购物狂欢节的临近&#xff0c;消费者们纷纷开始关注各类好物&#xff0c;期待在这一天能够以最优惠的价格入手心仪的商品&#xff0c;在这个特殊的时刻&#xff0c;我们为大家盘点了五大性价比拉满的闭眼入好物&#xff0c;这些产品不仅品质卓越&#xff0c;而…...

Hbase日常运维

1 Hbase日常运维 1.1 监控Hbase运行状况 1.1.1 操作系统 1.1.1.1 IO 群集网络IO&#xff0c;磁盘IO&#xff0c;HDFS IO IO越大说明文件读写操作越多。当IO突然增加时&#xff0c;有可能&#xff1a;1.compact队列较大&#xff0c;集群正在进行大量压缩操作。 2.正在执行…...

鸿蒙开发的基本技术栈及学习路线

随着智能终端设备的不断普及与技术的进步&#xff0c;华为推出的鸿蒙操作系统&#xff08;HarmonyOS&#xff09;迅速引起了全球的关注。作为一个面向多种设备的分布式操作系统&#xff0c;鸿蒙不仅支持手机、平板、智能穿戴设备等&#xff0c;还支持IoT&#xff08;物联网&…...

【算法】反向传播算法

David Rumelhart 是人工智能领域的先驱之一&#xff0c;他与 James McClelland 等人在1986年通过其著作《Parallel Distributed Processing: Explorations in the Microstructure of Cognition》详细介绍了反向传播算法&#xff08;Backpropagation&#xff09;&#xff0c;这一…...

外贸非洲市场要如何开发

刚不久前中非合作峰会论坛之后&#xff0c;取消了非洲33国的进口关税&#xff0c;中非贸易一直以来都还不错&#xff0c;这次应该会更上一个台阶。今天就来给大家分享一下&#xff0c;关于非洲市场的一些分析和开发方法。 一、非洲市场情况 非洲是一个广阔的大陆&#xff0c;由…...

python去除空格join()

sinput().split() print( .join(s)) input().split()的作用&#xff1a; split()是字符串对象的方法。当对一个字符串调用split()方法时&#xff0c;它会根据指定的分隔符将字符串分割成多个子字符串&#xff0c;并将这些子字符串以列表的形式返回。如果不指定分隔符&#xf…...

git push错误:Out of memory, malloc failed (tried toallocate 947912704 bytes)

目录 一、错误截图 二、解决办法 一、错误截图 因项目文件过大&#xff0c;http.postBuffer设置的内存不够&#xff0c;所以报错。 二、解决办法 打开cmd窗口&#xff0c;执行如下命令即可 git config --global http.postBuffer 1024000000 如图所示 执行完成以后&#…...

web平台搭建-LAMP(CentOS-7)

一. 准备工作 环境要求&#xff1a; 操作系统&#xff1a;CentOS 7.X 64位 网络配置&#xff1a;nmtui字符终端图形管理工具或者直接编辑配置文件 关闭SELinux和firewalld防火墙 防火墙&#xff1a; 临时关闭&#xff1a;systemctl stop firewalld 永久关闭&#xff1a;systemc…...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)

说明&#xff1a; 想象一下&#xff0c;你正在用eNSP搭建一个虚拟的网络世界&#xff0c;里面有虚拟的路由器、交换机、电脑&#xff08;PC&#xff09;等等。这些设备都在你的电脑里面“运行”&#xff0c;它们之间可以互相通信&#xff0c;就像一个封闭的小王国。 但是&#…...

OpenLayers 可视化之热力图

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 热力图&#xff08;Heatmap&#xff09;又叫热点图&#xff0c;是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...

Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件

今天呢&#xff0c;博主的学习进度也是步入了Java Mybatis 框架&#xff0c;目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学&#xff0c;希望能对大家有所帮助&#xff0c;也特别欢迎大家指点不足之处&#xff0c;小生很乐意接受正确的建议&…...

基础测试工具使用经验

背景 vtune&#xff0c;perf, nsight system等基础测试工具&#xff0c;都是用过的&#xff0c;但是没有记录&#xff0c;都逐渐忘了。所以写这篇博客总结记录一下&#xff0c;只要以后发现新的用法&#xff0c;就记得来编辑补充一下 perf 比较基础的用法&#xff1a; 先改这…...

数据库分批入库

今天在工作中&#xff0c;遇到一个问题&#xff0c;就是分批查询的时候&#xff0c;由于批次过大导致出现了一些问题&#xff0c;一下是问题描述和解决方案&#xff1a; 示例&#xff1a; // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...

JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作

一、上下文切换 即使单核CPU也可以进行多线程执行代码&#xff0c;CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短&#xff0c;所以CPU会不断地切换线程执行&#xff0c;从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...

什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南

文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/55aefaea8a9f477e86d065227851fe3d.pn…...

Java多线程实现之Thread类深度解析

Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...

Linux --进程控制

本文从以下五个方面来初步认识进程控制&#xff1a; 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程&#xff0c;创建出来的进程就是子进程&#xff0c;原来的进程为父进程。…...