当前位置: 首页 > 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…...

Leather Dress Collection代码实例:Stable Diffusion XL适配LoRA迁移方案

Leather Dress Collection代码实例&#xff1a;Stable Diffusion XL适配LoRA迁移方案 1. 引言 如果你手头有一堆基于Stable Diffusion 1.5训练的LoRA模型&#xff0c;现在想用更强大的SDXL来生成图片&#xff0c;该怎么办&#xff1f;直接拿来用&#xff1f;大概率会出问题。…...

NEURAL MASK 在物联网(IoT)中的应用:边缘设备图像异常检测

NEURAL MASK 在物联网&#xff08;IoT&#xff09;中的应用&#xff1a;边缘设备图像异常检测 最近和几个做工厂设备维护的朋友聊天&#xff0c;他们都在头疼同一个问题&#xff1a;生产线上的摄像头越来越多&#xff0c;拍下来的视频数据像洪水一样往云端传&#xff0c;带宽费…...

Java框架开发短剧漫剧系统:后台管理与接口开发

本次开发采用Java主流框架组合&#xff0c;兼顾开发效率、可维护性与扩展性&#xff0c;核心技术选型&#xff1a;后端 SpringBoot 2.7.x&#xff08;简化配置、快速开发&#xff09; Spring Security&#xff08;权限管控&#xff09; MyBatis-Plus&#xff08;数据操作&#…...

【高精度气象】从“被动响应”到“主动预警”:电网综合气象灾害风险分析平台如何深度融入电网运行?

当覆冰监测装置提前72小时发出预警&#xff0c;当调度系统根据风速预测自动调整新能源出力——电网终于学会了在灾害来临前“抢跑”。2026年1月19日&#xff0c;江苏宿迁。500千伏泗澜线舞动在线监测装置自动触发预警。运维人员赶到现场时&#xff0c;导线刚刚开始轻微舞动&…...

CLIP ViT-H-14 Web界面功能演示:图像上传后自动显示1280维向量前10维

CLIP ViT-H-14 Web界面功能演示&#xff1a;图像上传后自动显示1280维向量前10维 1. 项目概述 CLIP ViT-H-14图像编码服务是一个基于先进视觉语言模型的图像特征提取工具。它能够将任意图像转换为1280维的特征向量&#xff0c;这些向量可以用于图像搜索、内容理解、相似度计算…...

小红书数据采集终极指南:零基础掌握Python爬虫实战技巧

小红书数据采集终极指南&#xff1a;零基础掌握Python爬虫实战技巧 【免费下载链接】xhs 基于小红书 Web 端进行的请求封装。https://reajason.github.io/xhs/ 项目地址: https://gitcode.com/gh_mirrors/xh/xhs 你是不是经常遇到这样的困扰&#xff1f;想分析小红书上的…...

ofa_image-caption_coco_distilled_en保姆级部署:NVIDIA Container Toolkit配置与GPU资源隔离实践

ofa_image-caption_coco_distilled_en保姆级部署&#xff1a;NVIDIA Container Toolkit配置与GPU资源隔离实践 安全声明&#xff1a;本文仅讨论技术实现方案&#xff0c;所有内容均基于公开技术文档和标准实践&#xff0c;不涉及任何敏感或受限制的技术应用。 1. 项目概述与核心…...

属电子信息类专业电子信息工程(Electronic Information Engineering,简称 EE)专业是什么?

属电子信息类专业电子信息工程&#xff08;Electronic Information Engineering&#xff0c;简称 EE&#xff09;专业是什么&#xff1f;电子信息工程&#xff08;Electronic Information Engineering&#xff0c;简称 EE&#xff09;简称电子信息&#xff0c;是现代科技领域的…...

【物联网毕设】基于Arduino与树莓派的智能鱼缸系统设计与实现

1. 项目背景与整体设计 养鱼爱好者都知道&#xff0c;维持鱼缸环境稳定是个技术活。水温忽高忽低、水中含氧量不足、水位不稳定等问题&#xff0c;都可能让心爱的观赏鱼遭殃。传统的人工监测方式不仅费时费力&#xff0c;还容易因疏忽造成损失。这正是我选择智能鱼缸系统作为毕…...

深入解析Unity粒子系统Particle System:生命周期控制模块实战指南

1. 粒子系统生命周期控制模块概览 在Unity中制作特效时&#xff0c;粒子系统的生命周期控制模块就像给粒子赋予了"成长轨迹"。想象你正在设计一场烟花表演——烟花弹射向高空&#xff08;初始速度&#xff09;&#xff0c;爆炸后火花四散&#xff08;速度变化&#x…...