为什么不要在循环,条件或嵌套函数中调用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、初始化
创建两个空数组:setters 和 state
将 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 第二次渲染

现在,firstName和lastName发生了错位,我们的状态存储变得不一致了。这就是为什么保持正确顺序的重要性。
总结
通过对 useState 的简单实现来理解 react hooks 的幕后实现逻辑。考虑将状态作为一组数组存在的模型,那么我们不该违反其对应的使用规则。
相关文章:
为什么不要在循环,条件或嵌套函数中调用hooks
为什么不要在循环,条件或嵌套函数中调用hooks 前言useState Hook 的工作原理具体实现1、初始化2、第一次渲染3、后续渲染4、事件处理简单代码实现 为什么顺序很重要Bad Component 第一次渲染Bad Component 第二次渲染 总结 前言 自从 React 推出 hooks 的 API 后&a…...
将成功请求的数据 放入apipost接口测试工具,发送给后端后,部分符号丢失
将成功请求的数据 放入apipost接口测试工具,发送给后端后,部分符号丢失 apipost、接口测试、符号、丢失、错乱、变成空格背景 做CA对接,保存CA系统的校验数据,需要模仿前端请求调起接口,以便测试功能完整性。 问题描…...
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实现详解
引言 在分布式系统中,消息传递是系统组件间通信的重要方式,而确保消息在传递过程中只被消费一次是一个关键问题。如果一个消息被多次消费,可能会导致业务逻辑重复执行,进而产生数据不一致、错误操作等问题。特别是在金融、电商等…...
Web3技术在元宇宙中的应用:从区块链到智能合约
随着元宇宙的兴起,Web3技术正逐渐成为其基础,推动着数字空间的重塑。元宇宙不仅是一个虚拟世界,它还代表着一个由去中心化技术驱动的新生态系统。在这个系统中,区块链和智能合约发挥着至关重要的作用,为用户提供安全、…...
关于QSizeGrip在ui界面存在布局的情况下的不显示问题
直接重写resizeEvent你会发现: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.如下图操作: 开始前的固有流程 按照如下步骤进行操作,于步骤3中更改Company Name等属性: 本案例分辨率可以如下设置,有能力者可根据需要自行调整: 场景制作…...
位运算--(二进制中1的个数)
位运算是计算机科学中一种高效的操作方式,常用于处理二进制数据。在Java中,位运算通常通过位移操作符和位与操作符实现。 当然位运算还有一些其他的奇淫巧计,今天介绍两个常用的位运算方法:返回整数x的二进制第k位的值和返回x的最…...
使用Docker和Macvlan驱动程序模拟跨主机跨网段通信
以下是使用Docker和Macvlan驱动程序模拟跨主机跨网段通信的架构图: #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 流程设计 操作步骤 离线数据集成(首页) → \rightarrow → 示例应用数据集成流程(边栏) → \rightarrow → 所有数据流程 → \rightarrow → webservice节点获取城市列表 → \rightarrow → 流程设计 …...
从入门到精通:QT 100个关键技术关键词
Qt基础概念 Qt Framework - 一个跨平台的C图形用户界面应用程序开发框架。它不仅提供了丰富的GUI组件,还包括网络、数据库访问、多媒体支持等功能。 Qt Creator - Qt官方提供的集成开发环境(IDE),集成了代码编辑器、项目管理工具、…...
2024年双十一值得入手的好物有哪些?五大性价比拉满闭眼入好物盘点
随着2024年双十一购物狂欢节的临近,消费者们纷纷开始关注各类好物,期待在这一天能够以最优惠的价格入手心仪的商品,在这个特殊的时刻,我们为大家盘点了五大性价比拉满的闭眼入好物,这些产品不仅品质卓越,而…...
Hbase日常运维
1 Hbase日常运维 1.1 监控Hbase运行状况 1.1.1 操作系统 1.1.1.1 IO 群集网络IO,磁盘IO,HDFS IO IO越大说明文件读写操作越多。当IO突然增加时,有可能:1.compact队列较大,集群正在进行大量压缩操作。 2.正在执行…...
鸿蒙开发的基本技术栈及学习路线
随着智能终端设备的不断普及与技术的进步,华为推出的鸿蒙操作系统(HarmonyOS)迅速引起了全球的关注。作为一个面向多种设备的分布式操作系统,鸿蒙不仅支持手机、平板、智能穿戴设备等,还支持IoT(物联网&…...
【算法】反向传播算法
David Rumelhart 是人工智能领域的先驱之一,他与 James McClelland 等人在1986年通过其著作《Parallel Distributed Processing: Explorations in the Microstructure of Cognition》详细介绍了反向传播算法(Backpropagation),这一…...
外贸非洲市场要如何开发
刚不久前中非合作峰会论坛之后,取消了非洲33国的进口关税,中非贸易一直以来都还不错,这次应该会更上一个台阶。今天就来给大家分享一下,关于非洲市场的一些分析和开发方法。 一、非洲市场情况 非洲是一个广阔的大陆,由…...
python去除空格join()
sinput().split() print( .join(s)) input().split()的作用: split()是字符串对象的方法。当对一个字符串调用split()方法时,它会根据指定的分隔符将字符串分割成多个子字符串,并将这些子字符串以列表的形式返回。如果不指定分隔符…...
git push错误:Out of memory, malloc failed (tried toallocate 947912704 bytes)
目录 一、错误截图 二、解决办法 一、错误截图 因项目文件过大,http.postBuffer设置的内存不够,所以报错。 二、解决办法 打开cmd窗口,执行如下命令即可 git config --global http.postBuffer 1024000000 如图所示 执行完成以后&#…...
web平台搭建-LAMP(CentOS-7)
一. 准备工作 环境要求: 操作系统:CentOS 7.X 64位 网络配置:nmtui字符终端图形管理工具或者直接编辑配置文件 关闭SELinux和firewalld防火墙 防火墙: 临时关闭:systemctl stop firewalld 永久关闭:systemc…...
深度学习在微纳光子学中的应用
深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向: 逆向设计 通过神经网络快速预测微纳结构的光学响应,替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...
2.Vue编写一个app
1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...
DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI
前一阵子在百度 AI 开发者大会上,看到基于小智 AI DIY 玩具的演示,感觉有点意思,想着自己也来试试。 如果只是想烧录现成的固件,乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外,还提供了基于网页版的 ESP LA…...
第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词
Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵,其中每行,每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid,其中有多少个 3 3 的 “幻方” 子矩阵&am…...
【Oracle】分区表
个人主页:Guiat 归属专栏:Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...
LeetCode - 199. 二叉树的右视图
题目 199. 二叉树的右视图 - 力扣(LeetCode) 思路 右视图是指从树的右侧看,对于每一层,只能看到该层最右边的节点。实现思路是: 使用深度优先搜索(DFS)按照"根-右-左"的顺序遍历树记录每个节点的深度对于…...
免费数学几何作图web平台
光锐软件免费数学工具,maths,数学制图,数学作图,几何作图,几何,AR开发,AR教育,增强现实,软件公司,XR,MR,VR,虚拟仿真,虚拟现实,混合现实,教育科技产品,职业模拟培训,高保真VR场景,结构互动课件,元宇宙http://xaglare.c…...
逻辑回归暴力训练预测金融欺诈
简述 「使用逻辑回归暴力预测金融欺诈,并不断增加特征维度持续测试」的做法,体现了一种逐步建模与迭代验证的实验思路,在金融欺诈检测中非常有价值,本文作为一篇回顾性记录了早年间公司给某行做反欺诈预测用到的技术和思路。百度…...
从 GreenPlum 到镜舟数据库:杭银消费金融湖仓一体转型实践
作者:吴岐诗,杭银消费金融大数据应用开发工程师 本文整理自杭银消费金融大数据应用开发工程师在StarRocks Summit Asia 2024的分享 引言:融合数据湖与数仓的创新之路 在数字金融时代,数据已成为金融机构的核心竞争力。杭银消费金…...
MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)
macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 🍺 最新版brew安装慢到怀疑人生?别怕,教你轻松起飞! 最近Homebrew更新至最新版,每次执行 brew 命令时都会自动从官方地址 https://formulae.…...
