(React Hooks)前端八股文修炼Day9

一 对 React Hook 的理解,它的实现原理是什么
React Hooks是React 16.8版本中引入的一个特性,它允许你在不编写类组件的情况下,使用state以及其他的React特性。Hooks的出现主要是为了解决类组件的一些问题,如复杂组件难以理解、难以重用状态逻辑等,并且还能够捕获组件中的副作用。
常见的React Hooks:
useState: 允许你在函数组件中添加state。useEffect: 用来处理副作用,类似于类组件的componentDidMount、componentDidUpdate和componentWillUnmount生命周期方法的组合。useContext: 允许你访问Context对象,并订阅其变化。useReducer: 为状态更新提供更复杂的逻辑,类似于Redux中的reducer。useCallback: 返回一个记忆化的回调函数。useMemo: 返回一个记忆化的值。useRef: 返回一个可变的ref对象。useLayoutEffect: 其作用与useEffect相似,但它会在所有的DOM更改之后同步触发重渲染。useImperativeHandle: 自定义使用ref时公开给父组件的实例值。useDebugValue: 在React开发者工具中显示自定义的hook标签。
实现原理:
Hooks的工作原理基于JavaScript的闭包机制。当你在函数组件中调用Hooks时,React内部会维护一个状态数组,每次渲染时,Hooks都会按顺序读取和更新这个数组中的状态。因此,每个Hook都必须在组件的顶层调用,这样React才能够正确地关联Hook和对应的状态。
以下是Hooks的一些关键实现原理:
-
状态存储: React为每个组件实例维护一个状态数组。当你使用
useState或useReducer时,它会在此数组中为每个state分配一个位置。 -
更新调度: 当你更新一个state,React会标记组件为dirty并安排一次重新渲染。在下一次组件渲染时,
useState和useEffect等Hooks将会使用最新的state和props。 -
闭包: Hooks依赖闭包来捕获每一次渲染时的props和state,这也是每次更新可以获取到正确state的原因。
-
Hook的规则: React要求Hooks必须在函数的最顶层调用,不能在循环、条件或嵌套函数中调用。这是因为React根据Hooks的调用顺序来存储和检索状态。
-
调试: React提供了
useDebugValue来在React开发者工具中显示自定义Hook的内部状态,使得调试更加方便。 -
组件生命周期:
useEffect和useLayoutEffectHooks使得你能够在函数组件中执行副作用,并且可以通过返回一个清理函数来执行清理操作,类似于类组件中的componentWillUnmount。
Hooks的这种设计允许你以一种更函数式的方式来编写组件,同时使得状态逻辑更容易被分割和重用。Hooks的实现是React团队在框架内部进行了大量的工作,但开发者可以通过上述Hooks以一种简单直观的方式来利用它们的能力。
二 为什么 useState 要使用数组而不是对象
useState 是 React 提供的一个 Hook,使得开发者可以在函数组件中存储和管理状态。使用 useState,你会得到一对值:当前状态和一个更新它的函数。这个 Hook 使用数组结构的主要原因包括:
1. 返回值命名自由
当你使用数组结构时,你可以自由地给状态和更新函数命名,而不是被迫使用对象的键。这提供了更大的灵活性和简洁性。
const [value, setValue] = useState(initialValue);
在这个例子中,value 和 setValue 可以根据你的选择自由命名,例如:
const [count, setCount] = useState(0);
2. 减少嵌套和避免合并状态
如果 useState 返回的是一个对象,你可能需要合并现有状态和新状态,这样更新状态时就更容易出错。数组结构的 useState 鼓励你将状态分割成尽可能多的独立的状态变量,减少了状态更新时的复杂性。
如果是对象,更新状态可能需要这样:
this.setState(prevState => {return { ...prevState, key: newValue };
});
而用 useState,更新状态只需这样:
setValue(newValue);
3. 保持API的简洁和一致性
返回数组意味着 useState 总是返回两个值:当前的状态值和一个函数,用于更新状态。这种一致性无论何时使用 useState 都是不变的,因此易于理解和预测。
4. 保持与React Class组件的setState的行为不同
setState 在类组件中通常是合并状态,而 useState 鼓励你使用多个状态钩子来独立地管理不同的状态变量,这使得状态逻辑更清晰。
5. Hook的设计灵感源自函数式编程
函数式编程中经常会用到元组(Tuple),它是编程中一种将多个值组合成一个紧凑形式的方式。React的Hooks API设计者显然受到了函数式编程中使用元组的启发。
总结
使用数组而不是对象返回 useState 的值,使得状态钩子的使用更加灵活、简单和直观。它鼓励将状态分解为较小的、独立的片段,这有助于组件的维护和避免不必要的复杂性。同时,它也与React的函数式编程哲学保持一致。
三 React Hooks解决了哪些问题
React Hooks 的引入是为了解决 React 开发中的一系列问题和挑战,这些问题主要集中在类组件的使用和函数组件的限制方面。下面是 React Hooks 解决的一些主要问题:
1. 复用状态逻辑的难题
在 Hooks 出现之前,复用组件之间的状态逻辑通常需要依靠高阶组件(HOCs)或渲染道具(Render Props)模式。这些模式虽然功能强大,但容易使组件树变得复杂和深层,难以理解和维护。通过自定义 Hooks,开发者可以更容易地在组件之间共享状态逻辑,而不需要修改组件结构。
2. 复杂组件变得难以理解
类组件中,相关逻辑往往分散在不同的生命周期方法中,这使得跟踪组件的行为变得困难。Hooks 允许将相关代码组织在一起,无论它们是用于数据获取、订阅、还是其他副作用,都可以在一个 useEffect 内部管理,这样使得逻辑更加集中和清晰。
3. 难以理解的 this 关键字
在类组件中,this 关键字的行为经常让人困惑,尤其是在事件处理器和回调函数中。函数组件加上 Hooks 使得你可以避免使用 this,从而让代码更容易编写和理解。
4. 逐渐消失的组件生命周期
随着 React 的发展,一些生命周期方法已经被弃用,并且新的异步渲染模式使得一些生命周期方法的行为变得不可预测。Hooks 提供了一种更加直接和清晰的方式来处理副作用(例如,useEffect),不再依赖于生命周期方法。
5. 函数组件的局限性
在 Hooks 出现之前,函数组件被认为是无状态组件,仅适用于简单的展示逻辑。对于需要状态管理、生命周期方法或性能优化的场合,开发者不得不使用类组件。Hooks 的引入使得函数组件拥有了类组件的几乎所有能力,同时保持了更简洁的语法和更好的可读性。
6. 大型组件重构难题
在使用类组件开发大型应用时,重构或拆分复杂组件往往非常困难。Hooks 允许开发者以更小、更可管理的单元组织代码,使得重构和测试变得更加容易。
通过解决这些问题,React Hooks 提升了 React 开发的体验,使代码更加清晰简洁,提高了开发效率和应用性能。
四 React Hook 的使用限制有哪些
React Hooks 是强大的,但使用它们时需要遵守一些重要的规则和限制。这些规则确保了 Hooks 的正确使用和组件的正确行为。以下是使用 React Hooks 时的主要限制和规则:
只能在函数组件的顶层调用 Hooks
Hooks 必须在函数组件的最顶层调用,不能在循环、条件语句或嵌套函数中调用。这是因为 React 依赖于 Hooks 调用的顺序,如果在这些JavaScript结构中使用Hooks,那么Hooks的调用顺序就会变得不确定。
只能在 React 函数组件或自定义 Hooks 中调用 Hooks
不能在普通的 JavaScript 函数中调用 Hooks。Hooks 是特定于 React 的,它们依靠 React 的上下文来正确工作。如果你想在多个组件之间重用有状态逻辑,可以把它放到自定义 Hook 中。
不要在循环、条件或嵌套函数中调用 Hooks
如前所述,Hooks 应该总是在组件的最顶层调用,以确保每次组件渲染时 Hooks 的调用顺序保持一致。条件语句、循环或嵌套函数可能会使得Hooks的调用次序发生变化,从而破坏其内部状态的管理。
为 Hooks 提供正确的依赖项
对于 useEffect、useMemo、useCallback 这类需要依赖数组的 Hooks,确保你已经正确地指定了依赖项。遗漏依赖项或者提供错误的依赖项都可能导致不可预测的行为。
避免在常规函数中调用 Hooks
如果你在常规的 JavaScript 函数中调用 Hooks(这里指的不是函数组件或自定义 Hooks),就会打破规则。Hooks 应该只在函数组件或者自定义 Hooks 中使用。
使用 ESLint 插件来强制执行这些规则
React 团队提供了一个 ESLint 插件 (eslint-plugin-react-hooks),它能帮助开发人员识别违反上述规则的代码。这个插件能够在开发过程中检测违反 Hooks 规则的情况,以减少错误。
理解 Hooks 的闭包行为
Hooks 如 useEffect 会捕获定义时的状态和道具。如果你不小心,可能会在异步操作中引用过时的状态或道具,导致难以发现的错误。因此,使用更新函数形式的 useState 和正确的依赖项列表十分重要。
遵守以上规则,可以保证你的 Hooks 正确地工作,并且你编写的函数组件行为符合预期。
五 useEffect 与 useLayoutEffect 的区别
useEffect 和 useLayoutEffect 都是 React 的内置 Hooks,它们的作用是让你在函数组件中执行副作用操作。尽管它们的用途相似,但是它们在何时执行副作用方面存在一些关键的区别:
useEffect
useEffect在组件渲染到屏幕之后异步执行副作用。- 通常情况下,推荐使用
useEffect来执行副作用,因为它不会阻塞浏览器更新屏幕,这可以让用户感觉到响应更快。 - 常用于数据获取、订阅、或者直接操作 DOM 等场景。
useLayoutEffect
useLayoutEffect的调用时机与componentDidMount和componentDidUpdate生命周期方法更为一致,它在 DOM 更新完毕后立即同步执行副作用,但在浏览器进行任何绘制之前。useLayoutEffect通常用于需要同步更新 DOM 或者需要在浏览器绘制之前进行 DOM 操作来避免闪烁的情况。- 因为
useLayoutEffect会在所有的 DOM 变更之后同步执行,如果你的副作用函数执行较慢,它可能会导致帧率下降。
总结来说,如果你的副作用是跟 DOM 操作或者布局计算有关,且需要同步执行的(比如阅读布局并同步重新渲染的情况),那么使用 useLayoutEffect。否则,推荐使用 useEffect。
六 React Hooks在平时开发中需要注意的问题和原因
在使用 React Hooks 进行开发时,需要注意一系列问题,以确保代码的健壯性、性能和可维护性。以下是一些在日常开发中应该注意的关键点及其原因:
1. 遵守规则
- 不要在循环、条件语句或嵌套函数中调用 Hooks:这可能会导致 Hooks 的调用顺序发生变化,从而破坏 React 内部的状态管理。
- 只在函数组件或自定义 Hooks 中使用 Hooks:保持 Hooks 的使用环境一致性,避免在非 React 函数中产生不可预期的行为。
2. 依赖列表的正确使用
- 在
useEffect、useMemo和useCallback中正确管理依赖列表:省略依赖或错误地添加依赖可能会导致无限循环、过度渲染或陈旧闭包问题。 - 避免频繁变化的对象或函数作为依赖:这可能会导致不必要的副作用或计算被频繁触发。使用
useMemo和useCallback来优化这些依赖。
3. 性能优化
- 使用
React.memo、useMemo和useCallback来避免不必要的渲染:但要注意过度优化。合理使用这些工具来优化真正需要优化的场景。 - 谨慎使用大型对象或复杂计算作为依赖:考虑将状态拆分或使用
useReducer来管理复杂的组件状态。
4. 清理副作用
- 在
useEffect中返回清理函数:这对于取消订阅、清除定时器等是必要的,以避免内存泄漏和其他副作用。
5. 自定义 Hooks 的正确使用
- 合理封装和复用逻辑:通过自定义 Hooks 封装共享逻辑,但要保持其独立性和复用性,避免创建过于具体或耦合的自定义 Hooks。
6. 理解闭包陷阱
- 在使用函数式更新和延迟执行逻辑时注意闭包陷阱:特别是在
useEffect中访问的状态和属性,可能会捕获到旧的闭包值。使用函数式更新或者将依赖列入依赖列表来解决。
7. 状态逻辑的分割
- 避免在一个组件中使用过多的状态和副作用:考虑将逻辑分割到不同的组件或自定义 Hooks 中,以保持组件的清晰和可管理性。
遵循以上准则,可以帮助你更高效和安全地利用 React Hooks 构建应用,避免常见的陷阱和性能问题。
七 React Hooks 和生命周期的关系
React Hooks 的引入在 React 开发中标志着一个重要的转折点。在 Hooks 出现之前,React 组件的状态管理和副作用处理主要依赖于类组件和其生命周期方法。Hooks 的出现使得在无需编写类组件的情况下,也能使用状态管理和副作用等特性,极大地增强了函数组件的能力。这里,我们将探讨 React Hooks 和生命周期方法之间的关系:
生命周期方法
在类组件中,生命周期方法用于在组件不同阶段执行代码。常用的生命周期方法包括:
componentDidMount:组件挂载(插入 DOM 树)后执行componentDidUpdate:组件更新后执行componentWillUnmount:组件卸载(从 DOM 树移除)前执行shouldComponentUpdate、getDerivedStateFromProps、getSnapshotBeforeUpdate等用于特定的优化和操作
React Hooks 与生命周期的对应
React Hooks 使函数组件能够使用某些与类组件生命周期方法相对应的功能:
useState:提供状态管理,相当于类组件的this.state和this.setState。useEffect:- 可以看作是
componentDidMount、componentDidUpdate和componentWillUnmount的结合。通过在函数组件中调用useEffect,可以在组件渲染到屏幕后执行副作用操作,也可以通过返回一个清理函数来执行类似componentWillUnmount的逻辑。 - 对于需要精确控制副作用执行时机的场景,
useEffect的第二个参数(依赖数组)提供了这种灵活性,允许组件仅对特定状态的改变作出响应。
- 可以看作是
useLayoutEffect:- 与
useEffect类似,但它在 DOM 更新完成后同步执行副作用,这在处理 DOM 布局和样式时特别有用,因为它避免了可能的闪烁问题,相当于componentDidMount和componentDidUpdate的同步版。
- 与
useMemo和useCallback:- 这两个 Hook 能够帮助你在组件重新渲染时优化性能,它们通过记住计算结果或函数,避免不必要的重新计算或创建,可以在某种程度上与
shouldComponentUpdate或React.memo进行对比。
- 这两个 Hook 能够帮助你在组件重新渲染时优化性能,它们通过记住计算结果或函数,避免不必要的重新计算或创建,可以在某种程度上与
通过这些 Hooks,React 不仅简化了状态管理和副作用的处理,也使得在不编写类组件的情况下实现复杂组件成为可能。这种转变促进了函数式编程在 React 开发中的应用,提高了代码的模块化和重用性。
相关文章:
(React Hooks)前端八股文修炼Day9
一 对 React Hook 的理解,它的实现原理是什么 React Hooks是React 16.8版本中引入的一个特性,它允许你在不编写类组件的情况下,使用state以及其他的React特性。Hooks的出现主要是为了解决类组件的一些问题,如复杂组件难以理解、难…...
工厂方法模式:灵活的创建对象实例
在软件开发中,我们经常需要创建对象,但直接new一个实例可能会导致代码的耦合性增加,降低了代码的灵活性和可维护性。工厂方法模式(Factory Method Pattern)是一种创建型设计模式,它提供了一种创建对象的接口…...
vue-codeirror编辑器vue3中的使用
vue-codeirror编辑器vue3中的使用 <script lang"ts" setup> import { ref,reactive } from vue; import { Codemirror } from "vue-codemirror"; import { oneDark } from "codemirror/theme-one-dark"; import { json } from codemirror/…...
搭建python编译环境
目录 1.安装依赖包 2.安装失败进行换源 3. 更新系统 通过C 语言调用 Python 代码,需要先安装 libpython3 的 dev 依赖库(不同的 ubuntu 版本下, python 版本 可能会有差异, 比如ubuntu 22.04 里是 libpython3.10-dev )…...
微信小程序登录流程
文章目录 1. 用户触发登录操作2. 获取临时登录凭证3. 发送登录凭证到服务器4. 后端使用 code 获取 session_key 和 openid5. 后端保存 session_key 和 openid 返回token6. 前端保存登录态 1. 用户触发登录操作 用户在小程序内部点击登录按钮或进行需要登录权限的操作ÿ…...
FPGA + 图像处理(三)生成3x3像素矩阵
前言 生成NxN的像素矩阵是对图像进行各类滤波操作的基本前提,本文介绍一种通过bram生成3x3矩阵的方法。 程序 生成bram核 因为本文介绍的是基于bram生成的3x3像素矩阵,所以要先生成两个bram核,用于缓存前两行图像数据 在 IP catalog中选…...
Redis安装说明2
Redis安装说明 1.3.2.指定配置启动 如果要让Redis以后台方式启动,则必须修改Redis配置文件,就在我们之前解压的redis安装包下(/usr/local/src/redis-6.2.6),名字叫redis.conf: 我们先将这个配置文件备份一…...
ArcGIS10.8保姆式安装教程
ArcGIS 10.8是一款非常强大的地理信息系统软件,用于创建、管理、分析和可视化地理数据。以下是ArcGIS 10.8的详细安装教程: 确保系统满足安装要求 在开始安装之前,请确保您的计算机满足以下系统要求: 操作系统:Windo…...
设计原则、设计模式、设计模式项目实战
设计原则 封装、继承、多态、抽象分别可以解决哪些编程问题 封装:也叫做信息隐藏或数据保护访问。数据 通过暴露有限的访问接口,授权外部仅能通过类提供接口访问,对内的类private私有化属性,通过封装简化操作,让用户更…...
【Redis】解决List类型的消息可靠性问题
前言 平时做后端开发时,如果需要用到消息队列功能,但公司的IT环境又没有提供专业的队列软件(RabitMQ/Kafka…),那么在简单且要求不高的场景下,可以使用 Redis 的List数据类型来做消息队列。 但List类型有…...
挑战30天C++基本入门(DAY8--树)[part 3](速通哦~)
#上一章我们把搜索二叉树的知识给传授完毕,如果认真的看下去并且手打了几遍,基本上内部的逻辑还是可以理解的,那我们现在就截至继续学习树的一些重要知识啦~~ 树高怎么求呀?如果用上一次学的层次遍历来求树高,有点小题…...
在虚拟机尝试一次用启动盘重装系统
在虚拟机尝试一次用启动盘重装系统 没有自己重装过系统,也不敢对自己的笔记本下手,用虚拟机重装玩玩试试。 先设置成u盘启动 从boot中选择相应的创建的硬盘即可(刚刚突然发现图片不能上传了,经过乱七八糟的尝试后,开一…...
力扣347. 前 K 个高频元素
思路:记录元素出现的次数用map; 要维护前k个元素,不至于把所有元素都排序再取前k个,而是新建一个堆,用小根堆存放前k个最大的数。 为什么是小根堆?因为堆每次出数据时只出堆顶,每次把当前最小的…...
SCP 从Linux快速下载文件到Windows本地
需求:通过mobaxterm将大文件拖动到windows本地速度太慢。 环境:本地是Windows,安装了Git。 操作:进入文件夹内,鼠标右键,点击Git Bash here,然后输入命令即可。这样的话,其实自己本…...
plasmo内容UI组件层级过高导致页面展示错乱
我使用plasmo写了一个行内样式的UI组件,但是放到页面上之后,会和下拉组件出现层级错乱,看了一下样式,吓我一跳:层级竟然设置的如此之高 所以就需要将层级设置低一点: #plasmo-shadow-container {z-index: …...
《QT实用小工具·十一》Echart图表JS交互之仪表盘
1、概述 源码放在文章末尾 该项目为Echart图表JS交互之炫酷的仪表盘,可以用鼠标实时改变仪表盘的读数。 下面为demo演示: 该项目部分代码如下: #include "widget.h" #include "ui_widget.h" #include "qurl.h&q…...
深入浅出理解ArrayBuffer对象TypedArray和DataView视图
目录 举例理解 1. ArrayBuffer对象 2. TypedArray 3. DataView 总结 具体讲解 1. ArrayBuffer对象 2. TypedArray 3. DataView 注意事项 举例理解 先举个简单的例子理解ArrayBuffer对象TypedArray和DataView视图的概念和之间的关系 1. ArrayBuffer对象 想象一个场景…...
人工智能 - 服务于谁?
人工智能服务于谁? 人工智能服务于生存,其最终就是服务于战争(热战、技术战、经济战) 反正就是为了活着而战的决策。 既然人工智能所有结果,来自大数据的分挖掘(分析)也就是数据的应用&#x…...
软考高级架构师:嵌入式系统的内核架构
作者:明明如月学长, CSDN 博客专家,大厂高级 Java 工程师,《性能优化方法论》作者、《解锁大厂思维:剖析《阿里巴巴Java开发手册》》、《再学经典:《Effective Java》独家解析》专栏作者。 热门文章推荐&am…...
分布式锁实战
4、分布式锁 4.1 、基本原理和实现方式对比 分布式锁:满足分布式系统或集群模式下多进程可见并且互斥的锁。 分布式锁的核心思想就是让大家都使用同一把锁,只要大家使用的是同一把锁,那么我们就能锁住线程,不让线程进行&#x…...
大话软工笔记—需求分析概述
需求分析,就是要对需求调研收集到的资料信息逐个地进行拆分、研究,从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要,后续设计的依据主要来自于需求分析的成果,包括: 项目的目的…...
【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器
一.自适应梯度算法Adagrad概述 Adagrad(Adaptive Gradient Algorithm)是一种自适应学习率的优化算法,由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率,适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...
uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖
在前面的练习中,每个页面需要使用ref,onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入,需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...
深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法
深入浅出:JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中,随机数的生成看似简单,却隐藏着许多玄机。无论是生成密码、加密密钥,还是创建安全令牌,随机数的质量直接关系到系统的安全性。Jav…...
macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用
文章目录 问题现象问题原因解决办法 问题现象 macOS启动台(Launchpad)多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显,都是Google家的办公全家桶。这些应用并不是通过独立安装的…...
新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...
WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)
一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解,适合用作学习或写简历项目背景说明。 🧠 一、概念简介:Solidity 合约开发 Solidity 是一种专门为 以太坊(Ethereum)平台编写智能合约的高级编…...
C++使用 new 来创建动态数组
问题: 不能使用变量定义数组大小 原因: 这是因为数组在内存中是连续存储的,编译器需要在编译阶段就确定数组的大小,以便正确地分配内存空间。如果允许使用变量来定义数组的大小,那么编译器就无法在编译时确定数组的大…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
