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

React的hook✅

为什么hook必须在组件内的顶层声明?

这是为了确保每次组件渲染时,Hooks 的调用顺序保持一致。React利用 hook 的调用顺序来跟踪各个 hook 的状态。每当一个函数组件被渲染时,所有的 hook 调用都是按照从上到下的顺序依次执行的。React 内部会维护一个状态列表,这个列表中的每个状态项都对应一个 hook 的调用,包括 useState、useEffect 等。当你调用 useState(initialValue) 时,React 会在内部为这个状态分配一个索引。该索引基于 hook 调用的顺序。例如,第一次调用 useState 时,它会在状态列表的第一个位置存储状态,第二次调用会在第二个位置存储,以此类推

考虑下面这个demo:

const Component = () => {const [count, setCount] = useState(0);const handleOfClick = () => setCount(count + 1);return <button onClick={setCount}>{count}</button>
}

Q:既然每次视图的更新都会重新执行整个函数,那必然会执行到const [count, setCount] = useState(0)这句代码。如果我在上一次更新中把count加到10,为什么在新的渲染周期中,React能记住这个10而不是传给useState的0呢?
A:当组件重新渲染时,React 会根据组件的调用顺序再次按顺序调用对应的 hook。这样,React 可以确保它能够始终访问到正确的状态。例如,当第二次渲染时,React 知道第一个 useState 是哪个状态,因为它在第一次渲染时已经分配了这个状态的索引,这个索引是靠hook调用的顺序产生的索引来追踪的。 所以如果在条件语句、循环或嵌套函数中调用 hook,可能会导致调用顺序的变化,从而产生不可预知的状态。


useImperativeHandle

useImperativeHandle通常是和forwardRef配合使用的,用来把子组件中的属性或者方法暴露给父组件,在进行组件的封装或者组件间的通信的时候常会使用。如下:

// 封装一个可拖拽的组件
const DragComponent = forwardRef((props: {children: React.ReactNode, // 求求你不要挂一个很复杂的组件进来🙏// other config...},ref // ref是必须的) => {// 复位const resetPosition = () => {// todo: 可以在父组件中调用,让这个可拖拽的组件在父组件中回到第一次渲染的位置}useImperativeHandle(ref, () => {resetPosition // 显式声明})return (<div>{props.children}</div>)}
)// 之后在某一个页面中使用它
const Page = () => {const dragRef = useRef(null)const handleOfClick = () => {dragRef.current?.reset();}return (<div><DragComponent ref={dragRef}/><button onClick={handleOfClick}>复位</button></div>)
}

useCallback

useCallBack用来缓存一个函数的引用,它常常配合memo使用以提高渲染的性能。

const Component = memo(({ count, setCount }: { count: number; setCount: () => void }
) => {console.log("CountComponeng render");return (<div><button onClick={() => setCount()}>CountComponeng: {count}</button></div>);})function App() {console.log("App render");const [parentCount, setParentCount] = useState(0);const [childCount, setChildCount] = useState(0);const addChildCount = useCallback(() => {setChildCount(childCount + 1);}, [childCount]);// 这样也行// const addChildCount = useCallback(() => {//     setChildCount((count) => count + 1);// }, []);return (<div id="app"><h1>Hello Vite + React!</h1><button onClick={() => setParentCount(parentCount + 1)}>parentCount: {parentCount}</button><CountComponeng setCount={setChildCount} count={childCount} /></div>);
}export default App;

useReducer

类似redux的更规范的写法,我用的还不多😢,权当记录(该说不说,确实优雅):

import React, { useReducer } from "react";// 定义初始状态
const initialState = { count: 0 };// 定义 reducer 函数
const reducer = (state, action) => {switch (action.type) {case "increment":return { count: state.count + 1 };case "decrement":return { count: state.count - 1 };default:return state;}
};const Counter = () => {// 使用 useReducerconst [state, dispatch] = useReducer(reducer, initialState);return (<div><p>Count: {state.count}</p><button onClick={() => dispatch({ type: "increment" })}>Increment</button><button onClick={() => dispatch({ type: "decrement" })}>Decrement</button></div>);
};
export default Counter;

setState的函数写法和变量写法

我有这样的代码:const [count, setCount] = useState(0),在普通的情况下setCount(count + 1)setCount((count) => count + 1)都能实现count加1并更新视图的操作。

但是,考虑下面这个demo:

const handleOfClick = () => {setCount(count+1)setCount(count+1)setCount(count+1)
}

每一次点击,count最终都只能加1而不能加3,这个React官网介绍的很清楚这里不多说。但是如果把上面的setCount(count+1)换成setCount((count) => count + 1),确实能实现点击一次就+3并更新视图的功能,因为这种函数的写法保证了在进行状态更新时,能够获取到最新的状态值,特别是在状态更新依赖于之前的状态值时,可以避免因为异步执行导致的潜在问题。
始终记住setState是异步的,而且不是没setState一次就更新一次视图的(涉及到React为了优化渲染性能而使用的批量更新策略)。

再考虑一个更普遍的场景:

const Page = () => {const [count, setCount] = useState(1);useEffect(() => {const scrollableContainer = document.getElementById("scrollable-container");scrollableContainer?.addEventListener("scroll", () => {setCount(count + 1)});}, [])return <div id="scroll-container">{count}</div>
}

你会发现,任凭你滚动的再快,count只会加到2,然后就一直不变了。因为此处的useEffect只会执行一次,当你使用 addEventListener 直接绑定事件时,你得到的是一个闭包。在这个闭包中,count 的值是在事件绑定时捕获的(1)
但是把setCount(count+1)换成setCount((count) => count + 1)就能每次滚动的时候都加1,因为它会接受当前状态作为参数,这样每次更新都会基于最新的状态进行计算,从而避免因为闭包问题导致的状态不正确的问题。

相关文章:

React的hook✅

为什么hook必须在组件内的顶层声明&#xff1f; 这是为了确保每次组件渲染时&#xff0c;Hooks 的调用顺序保持一致。React利用 hook 的调用顺序来跟踪各个 hook 的状态。每当一个函数组件被渲染时&#xff0c;所有的 hook 调用都是按照从上到下的顺序依次执行的。React 内部会…...

2024.5 AAAiGLaM:通过邻域分区和生成子图编码对领域知识图谱对齐的大型语言模型进行微调

GLaM: Fine-Tuning Large Language Models for Domain Knowledge Graph Alignment via Neighborhood Partitioning and Generative Subgraph Encoding 问题 如何将特定领域知识图谱直接整合进大语言模型&#xff08;LLM&#xff09;的表示中&#xff0c;以提高其在图数据上自…...

从熟练Python到入门学习C++(record 6)

基础之基础之最后一节-结构体 1.结构体的定义 结构体相对于自定义的一种新的变量类型。 四种定义方式&#xff0c;推荐第一种&#xff1b;第四种适合大量定义&#xff0c;也适合查找&#xff1b; #include <iostream> using namespace std; #include <string.h>…...

jenkins的安装(War包安装)

‌Jenkins是一个开源的持续集成工具&#xff0c;基于Java开发&#xff0c;主要用于监控持续的软件版本发布和测试项目。‌ 它提供了一个开放易用的平台&#xff0c;使软件项目能够实现持续集成。Jenkins的功能包括持续的软件版本发布和测试项目&#xff0c;以及监控外部调用执行…...

WPS 加载项开发说明wpsjs

wpsjs几个常用的CMD命令&#xff1a; 1.打开cmd输入命令测试版本号 npm -v 2.首次安装nodejs&#xff0c;npm默认国外镜像&#xff0c;包下载较慢时&#xff0c;可切换到国内镜像 //下载速度较慢时可切换国内镜像 npm config set registry https://registry.npmmirror.com …...

【Anomaly Detection论文阅读记录】PaDiM与PatchCore模型的区别与联系

PaDiM与PatchCore模型的区别与联系 背景介绍 PADIM(Pretrained Anomaly Detection via Image Matching)和 PatchCore 都是基于深度学习的异常检测方法,主要用于图像异常检测,尤其是在无监督学习设置下。 PADIM 是一种通过利用预训练的视觉模型(例如,ImageNet预训练的卷…...

uni-app Vue3语法实现微信小程序样式穿透uview-plus框架

1 问题描述 我在用 uni-app vue3 语法开发微信小程序时&#xff0c;在项目中使用了 uview-plus 这一开源 UI 框架。在使用 up-text 组件时&#xff0c;想要给它添加一些样式&#xff0c;之前了解到微信小程序存在样式隔离的问题&#xff0c;也在uview-plus官网-注意事项中找到…...

K8S基础概念和环境搭建

K8S的基础概念 1. 什么是K8S K8S的全称是Kubernetes K8S是一个开源的容器编排平台&#xff0c;用于自动化部署、扩缩、管理容器化应用程序。 2. 集群和节点 集群&#xff1a;K8S将多个机器统筹和管理起来&#xff0c;彼此保持通讯&#xff0c;这样的关系称之为集群。 节点…...

[服务器] 腾讯云服务器免费体验,成功部署网站

文章目录 概要整体架构流程概要 腾讯云服务器免费体验一个月。 整体架构流程 腾讯云服务器体验一个月, 选择预装 CentOS 7.5 首要最重要的是: 添加阿里云镜像。 不然国外源速度慢, 且容易失败。 yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/li…...

vue中el-select 模糊查询下拉两种方式

第一种&#xff1a;先获取所有下拉数据再模糊查询&#xff0c;效果如下 1&#xff0c;页面代码&#xff1a;speciesList是种类列表List, speciesId 是speciesList里面对应的id&#xff0c;filterable是过滤查询标签 <el-form-item label"种类" prop"species…...

深入解析PostgreSQL中的PL/pgSQL语法

在数据库管理系统中&#xff0c;PostgreSQL因其强大的功能和稳定性而受到广泛欢迎。其中&#xff0c;PL/pgSQL作为PostgreSQL的过程化语言&#xff0c;为用户提供了更为灵活和强大的编程能力。本文将深入解析PL/pgSQL的语法&#xff0c;帮助读者更好地掌握这门语言&#xff0c;…...

Vue 3集成海康Web插件实现视频监控

​&#x1f308;个人主页&#xff1a;前端青山 &#x1f525;系列专栏&#xff1a;组件封装篇 &#x1f516;人终将被年少不可得之物困其一生 依旧青山,本期给大家带来组件封装篇专栏内容:Vue 3集成海康Web插件实现视频监控 引言 最近在项目中使用了 Vue 3 结合海康Web插件来实…...

多目标优化算法:多目标蛇鹫优化算法(MOSBOA)求解DTLZ1-DTLZ9,提供完整MATLAB代码

一、蛇鹫优化算法 蛇鹫优化算法&#xff08;Secretary Bird Optimization Algorithm&#xff0c;简称SBOA&#xff09;由Youfa Fu等人于2024年4月发表在《Artificial Intelligence Review》期刊上的一种新型的元启发式算法。该算法旨在解决复杂工程优化问题&#xff0c;特别是…...

机器翻译基础与模型 之三:基于自注意力的模型

基于RNN和CNN的翻译模型&#xff0c;在处理文字序列时有个问题&#xff1a;它们对序列中不同位置之间的依赖关系的建模并不直接。以CNN的为例&#xff0c;如果要对长距离依赖进行描述&#xff0c;需要多层卷积操作&#xff0c;而且不同层之间信息传递也可能有损失&#xff0c;这…...

如何使用PCL处理ROS Bag文件中的点云数据并重新保存 ubuntu20.04

如何使用PCL处理ROS Bag文件中的点云数据并重新保存 要精确地处理ROS bag中的点云数据并使用PCL进行处理&#xff0c;再将处理后的数据保存回新的ROS bag文件&#xff0c;以下方案提供了详细、专业和严谨的步骤。 步骤 1: 环境设置 确保安装了ROS和PCL&#xff0c;并配置好环…...

背包问题(动态规划)

背包问题是一种组合优化的问题&#xff0c;它有多种变体&#xff0c;但最常见的两种是0/1背包问题和完全背包问题。 0/1背包问题 问题描述&#xff1a; 假设你有一个背包&#xff0c;背包的容量为W&#xff08;可以是重量或者体积等度量&#xff09;&#xff0c;同时有n个物品…...

从0开始学习机器学习--Day26--聚类算法

无监督学习(Unsupervised learning and introduction) 监督学习问题的样本 无监督学习样本 如图&#xff0c;可以看到两者的区别在于无监督学习的样本是没有标签的&#xff0c;换言之就是无监督学习不会赋予主观上的判断&#xff0c;需要算法自己去探寻区别&#xff0c;第二张…...

Vue3插槽v-slot使用方式

在 Vue 3 中&#xff0c;v-slot 是用来定义和使用插槽的指令。插槽是 Vue 的一个功能&#xff0c;允许你在组件内部定义占位内容&#xff0c;便于在父组件中提供动态内容。以下是 v-slot 的详细使用方法&#xff1a; 1. 基础使用 <template><BaseComponent><te…...

Axure二级菜单下拉交互实例

1.使用boxlabe进行基础布局 2.设置鼠标悬浮和选中状态 3.转换为动态面板 选中所有二级菜单,进行按钮组转换 选中所有二级菜单,进行动态面板转换 4.给用户管理增加显示/隐藏事件 1)选择toggle代表上拉和下拉切换加载 2)勾选Bring to Front,并选择Push/Pull Widgets代表收缩时…...

华为VPN技术

1.启动设备 2.配置IP地址 [FW1]int g1/0/0 [FW1-GigabitEthernet1/0/0]ip add 192.168.1.254 24 [FW1-GigabitEthernet1/0/0]int g1/0/1 [FW1-GigabitEthernet1/0/1]ip add 100.1.1.1 24 [FW1-GigabitEthernet1/0/1]service-manage ping permit [FW2]int g1/0/0 [FW2-Gi…...

docker详细操作--未完待续

docker介绍 docker官网: Docker&#xff1a;加速容器应用程序开发 harbor官网&#xff1a;Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台&#xff0c;用于将应用程序及其依赖项&#xff08;如库、运行时环…...

51c自动驾驶~合集58

我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留&#xff0c;CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制&#xff08;CCA-Attention&#xff09;&#xff0c;…...

树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法

树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源&#xff1a; http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作&#xff0c;无需更改相机配置。但是&#xff0c;一…...

C++:std::is_convertible

C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...

React Native 开发环境搭建(全平台详解)

React Native 开发环境搭建&#xff08;全平台详解&#xff09; 在开始使用 React Native 开发移动应用之前&#xff0c;正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南&#xff0c;涵盖 macOS 和 Windows 平台的配置步骤&#xff0c;如何在 Android 和 iOS…...

cf2117E

原题链接&#xff1a;https://codeforces.com/contest/2117/problem/E 题目背景&#xff1a; 给定两个数组a,b&#xff0c;可以执行多次以下操作&#xff1a;选择 i (1 < i < n - 1)&#xff0c;并设置 或&#xff0c;也可以在执行上述操作前执行一次删除任意 和 。求…...

数据链路层的主要功能是什么

数据链路层&#xff08;OSI模型第2层&#xff09;的核心功能是在相邻网络节点&#xff08;如交换机、主机&#xff09;间提供可靠的数据帧传输服务&#xff0c;主要职责包括&#xff1a; &#x1f511; 核心功能详解&#xff1a; 帧封装与解封装 封装&#xff1a; 将网络层下发…...

相机从app启动流程

一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...

Android15默认授权浮窗权限

我们经常有那种需求&#xff0c;客户需要定制的apk集成在ROM中&#xff0c;并且默认授予其【显示在其他应用的上层】权限&#xff0c;也就是我们常说的浮窗权限&#xff0c;那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...

OpenLayers 分屏对比(地图联动)

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能&#xff0c;和卷帘图层不一样的是&#xff0c;分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...