React中useState、useReducer与useRef
useState 详解
useState 是 React 函数组件中用于管理组件状态的 Hook。它提供了一种简洁的方式来在函数组件中添加状态,并在状态改变时触发组件重新渲染。以下是 useState 的详细解析:
一、基本概念
useState 是一个函数,它接收一个初始状态值作为参数,并返回一个数组。这个数组包含两个元素:当前状态值和一个用于更新该状态的函数。
二、语法与参数
const [state, setState] = useState(initialState);
- initialState:状态的初始值。可以是任何类型的值,包括数字、字符串、对象、数组等。
- 返回值:
useState返回一个数组,数组的第一个元素是当前的状态值(state),第二个元素是一个函数(setState),用于更新状态。
三、工作原理
- 初始化状态:当组件首次渲染时,
useState会使用传入的initialState参数来初始化状态。 - 更新状态:当调用
setState函数时,React 会将新的状态值与当前状态值进行比较。如果它们不相同,React 会重新渲染组件,并使用新的状态值。 - 合并更新:如果有多个
setState调用在同一个事件循环中发生(例如在setTimeout或Promise的回调中),React 会将它们合并成一个更新,以减少不必要的渲染次数。
四、使用示例
以下是一个简单的计数器示例,展示了如何使用 useState:
import React, { useState } from 'react'; function Counter() { const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> );
} export default Counter;
这个例子中,我们定义了一个 Counter 组件,它使用 useState 来管理一个名为 count 的状态。初始状态值设置为 0。当用户点击按钮时,setCount 函数被调用,并将 count 的值增加 1。由于状态发生了改变,React 会重新渲染组件,并显示更新后的计数值。
五、特性与优势
- 简洁性:
useState提供了一种简洁的方式来在函数组件中添加状态。 - 响应式更新:当状态发生改变时,React 会自动重新渲染组件,以确保视图与状态保持一致。
- 函数式更新:
setState函数可以接受一个函数作为参数,这个函数接收当前状态作为参数,并返回一个新的状态值。这种方式可以确保状态更新的原子性和一致性。 - 避免直接修改状态:React 推荐使用
setState函数来更新状态,而不是直接修改状态值。这是因为直接修改状态可能会导致组件状态与视图不一致,从而引发不可预测的行为。
六、注意事项
- 不要将状态存储在局部变量中:状态应该始终通过
useStateHook 来管理,而不是存储在局部变量中。否则,React 无法检测到状态的变化,也不会触发重新渲染。 - 避免在循环或条件语句中调用
useState:useState应该在组件的顶层调用,而不是在循环、条件语句或嵌套函数中调用。这是因为每次渲染时,useState的调用顺序和参数都应该保持不变。 - 不要过度使用状态:虽然状态是 React 应用的核心,但过度使用状态可能会导致组件变得复杂和难以维护。在可能的情况下,优先考虑使用 React 的其他特性(如 props、context 或 hooks)来传递数据和逻辑。
总之,useState 是 React 中一个非常重要的 Hook,它提供了一种简洁而强大的方式来管理函数组件中的状态。通过合理使用 useState,我们可以创建出响应式、可维护和可扩展的 React 应用
useRef详解
useRef是 React 提供的一个 Hook,它在函数组件中非常有用,主要用于以下几种场景:
一、访问 DOM 元素
useRef 可以用来获取并操作 DOM 元素,这在某些场景下非常有用,比如设置焦点、测量元素尺寸、执行动画等。使用 useRef 访问 DOM 元素的步骤如下:
- 调用
useRef并传入null或一个初始值来创建一个 ref 对象。 - 将这个 ref 对象附加到 React 元素的
ref属性上。 - 在组件的生命周期方法或事件处理函数中,通过
ref.current访问对应的 DOM 元素。
例如:
import React, { useRef, useEffect } from 'react'; function MyComponent() { const myDivRef = useRef(null); useEffect(() => { // 访问 DOM 元素并设置其背景颜色 if (myDivRef.current) { myDivRef.current.style.backgroundColor = 'lightblue'; } }, []); return <div ref={myDivRef}>这个 div 的背景颜色是通过 useRef 设置的。</div>;
}
二、存储任意可变值
useRef 还可以用来存储任意可变值,并且这些值的改变不会触发组件的重新渲染。这对于存储不需要触发渲染的逻辑状态或缓存数据非常有用。
例如,我们可以使用 useRef 来存储一个计数器,并在按钮点击事件中更新它:
import React, { useRef } from 'react'; function Counter() { const countRef = useRef(0); const handleClick = () => { countRef.current += 1; console.log(`计数值现在是: ${countRef.current}`); }; return <div><button onClick={handleClick}>增加计数</button></div>;
}
在这个例子中,countRef.current 存储了一个计数器,并且每次点击按钮时都会增加它的值。但是,由于 useRef 的更新不会触发组件的重新渲染,所以即使计数器的值改变了,组件也不会重新渲染。
三、缓存上一次的值
在某些情况下,我们可能需要缓存上一次渲染时的值,以便在后续的逻辑中使用。useRef 可以很好地满足这个需求,因为它在组件的整个生命周期内保持不变。
例如,在 useEffect 中,我们可以使用 useRef 来缓存上一次的状态值,以便在比较前后状态的变化时使用:
import React, { useRef, useState, useEffect } from 'react'; function App() { const [count, setCount] = useState(0); const prevCount = useRef(); useEffect(() => { // 存储更新前的数值,不会触发组件渲染 prevCount.current = count; }, [count]); // 注意:这里将 count 作为依赖项,确保每次 count 变化时都执行这个 effect return ( <div> <button onClick={() => setCount(count + 1)}>增加计数</button> <div>更新后的值: {count}</div> <div>更新前的值: {prevCount.current}</div> </div> );
}
在这个例子中,prevCount.current 缓存了上一次渲染时的 count 值,这样我们就可以在组件中方便地比较前后状态的变化了。
四、在自定义 Hook 中共享数据
useRef 还可以在自定义 Hook 中用来共享数据,使得多个组件可以共享同一个数据源。这对于实现某些全局状态管理或跨组件通信的场景非常有用。
例如,我们可以创建一个自定义 Hook 来管理一个计数器,并在多个组件中使用它:
import { useRef } from 'react'; function useSharedCounter(initialValue) { const counterRef = useRef(initialValue); const increment = () => { counterRef.current += 1; }; const decrement = () => { counterRef.current -= 1; }; return { count: counterRef.current, increment, decrement, };
} // 在组件中使用这个自定义 Hook
function CounterComponent1() { const { count, increment } = useSharedCounter(0); return ( <div> <p>Counter in Component 1: {count}</p> <button onClick={increment}>Increment</button> </div> );
} function CounterComponent2() { const { count, decrement } = useSharedCounter(100); // 注意:这里传入了一个不同的初始值,但实际上在多个组件中共享的是同一个 ref 对象,所以初始值只会在第一个组件挂载时生效 return ( <div> <p>Counter in Component 2: {count}</p> <button onClick={decrement}>Decrement</button> </div> );
}
在这个例子中,useSharedCounter 是一个自定义 Hook,它使用 useRef 来存储计数器的值,并提供了增加和减少计数器值的方法。CounterComponent1 和 CounterComponent2 都使用了这个自定义 Hook,并且它们共享了同一个计数器。这意味着无论哪个组件修改了计数器的值,其他组件都会立即反映这个变化。
注意事项
useRef创建的 ref 对象在组件的整个生命周期内保持不变。- 修改
ref.current的值不会触发组件的重新渲染。 - 不要在渲染期间写入或读取
ref.current,否则可能会使组件行为变得不可预测。 useRef通常用于直接访问和操作 DOM 元素或存储不需要触发渲染的逻辑状态。
综上所述,useRef 在 React 函数组件中是一个非常有用的 Hook,它提供了多种功能来满足不同的需求。
useReducer详解
useReducer是React中的一个Hook,它提供了一种更复杂的状态管理机制,适用于那些状态逻辑较为复杂、包含多个子值的情况。以下是对useReducer的详细解析:
一、基本概念
useReducer基于一个叫做reducer的函数来更新状态。Reducer接收当前的状态和一个表示要进行的操作的动作对象(action),并返回新的状态。
二、语法与参数
useReducer的基本语法为:
const [state, dispatch] = useReducer(reducer, initialState);
- reducer:一个函数,接收两个参数:当前状态(state)和要执行的动作(action)。它根据动作来决定如何更新状态,并返回更新后的状态。
- initialState:状态的初始值。
- 返回值:useReducer返回一个数组,数组的第一个元素是当前状态(state),第二个元素是一个函数(dispatch),用于向reducer发送动作。
三、工作原理
- 定义reducer函数:根据传入的动作类型来更新状态。
- 使用useReducer Hook:并传入reducer函数和初始状态。
- 在组件中使用dispatch函数:来发送动作,从而触发状态的更新。
四、使用示例
以下是一个简单的计数器示例,展示了如何使用useReducer:
import React, { useReducer } from 'react'; // 定义reducer函数
function counterReducer(state, action) { switch (action.type) { case 'increment': return { count: state.count + 1 }; case 'decrement': return { count: state.count - 1 }; default: throw new Error(); }
} // 定义初始状态
const initialState = { count: 0 }; // 定义计数器组件
function Counter() { const [state, dispatch] = useReducer(counterReducer, initialState); return ( <div> Count: {state.count} <button onClick={() => dispatch({ type: 'decrement' })}>-</button> <button onClick={() => dispatch({ type: 'increment' })}>+</button> </div> );
} export default Counter;
在这个例子中,我们定义了一个Counter组件,它使用useReducer来管理状态。state包含一个count属性,表示当前的计数值。dispatch函数用于发送动作,根据不同的动作类型来更新状态。当用户点击增加按钮时,我们调用dispatch({ type: 'increment' }),触发counterReducer中的increment动作,从而将计数器的值加一。同理,当用户点击减少按钮时,会触发decrement动作,将计数器的值减一。
五、特性与优势
- 复杂状态逻辑:对于涉及多个状态变量和复杂的更新逻辑的场景,使用useReducer可以更好地组织和维护代码。
- 可预测的状态更新:useReducer使用函数来更新状态,这使得状态更新更加可预测和易于理解。
- 更好的代码可读性:通过将状态更新逻辑拆分为独立的函数(reducer),可以提高代码的可读性和可维护性。
- 性能优化:在某些情况下,useReducer可以提供更好的性能,尤其是在处理大量状态更新时。
- 支持合并更新操作:useReducer支持合并多个更新操作,从而减少不必要的重新渲染。
六、与其他状态管理方法的比较
与useState相比,useReducer更适合处理包含多个子值的复杂状态逻辑,或者当下一个状态依赖于之前的状态时。它让状态管理逻辑外部化和中心化,使得逻辑更易于理解和维护,尤其是在大型组件或复杂交互中。
与useContext相比,useReducer主要关注于组件内部的状态管理,而useContext则更适合在不同组件之间共享状态。在实际开发中,可以根据项目的规模和复杂度来选择合适的状态管理方法。
总之,useReducer是React中一个强大的状态管理Hook,它提供了一种更简洁、更易于理解的方式来处理复杂的状态逻辑。在需要处理复杂状态的情况下,推荐使用useReducer来管理应用状态。
比较
| Hook | useState | useRef | useReducer |
|---|---|---|---|
| 主要用途 | 在函数组件中添加可变状态,管理需要触发渲染更新的状态数据 | 创建对值或DOM元素的持久引用,存储不需要触发重新渲染的数据 | 管理组件状态,适用于复杂的状态逻辑和多种操作类型 |
| 返回值 | 一个数组,包含当前状态的值和一个更新状态的函数 | 一个ref对象,其current属性被初始化为传给useRef的参数 | 当前状态和dispatch函数 |
| 触发渲染 | 当状态更新时,组件会重新渲染 | 不会触发组件的重新渲染 | 状态的更新不直接触发渲染,但可通过触发状态更新间接导致渲染 |
| 数据类型 | 通常用于处理基本数据类型,或使用useReducer处理复杂数据结构 | 可以存储任何可变的值,包括对象和函数 | 可以管理复杂的数据结构,如对象和数组 |
| 状态更新方式 | 使用返回的更新函数来更新状态,可以是同步或异步更新 | 直接修改ref对象的current属性来更新值 | 通过dispatch函数发送action给reducer函数,reducer根据action更新状态 |
| 初始值设置 | 接受一个初始值参数,这个值在组件的生命周期内只会被设置一次 | 初始值可以是任何值,并且可以在组件的生命周期内随时更新(但不触发渲染) | 需要定义一个初始状态,作为useReducer的第一个参数 |
| 适用场景 | 适用于简单的状态管理,当状态之间没有复杂的依赖关系时 | 适用于直接访问和操作DOM元素,或存储不需要触发渲染的可变数据 | 适用于复杂的状态逻辑,当状态之间有复杂的依赖关系或需要进行多种操作时 |
| 内存管理 | 状态在组件卸载时会被清除 | ref对象的current属性中的数据会在组件卸载后依然存在,直到组件被垃圾回收 | 与useState类似,状态在组件卸载时会被清除(但reducer逻辑和初始状态定义不受影响) |
相关文章:
React中useState、useReducer与useRef
useState 详解 useState 是 React 函数组件中用于管理组件状态的 Hook。它提供了一种简洁的方式来在函数组件中添加状态,并在状态改变时触发组件重新渲染。以下是 useState 的详细解析: 一、基本概念 useState 是一个函数,它接收一个初始状…...
ReGCL Rethinking Message Passingin Graph Contrastive Learning
AAAI24 推荐指数: #paper/⭐ 总体说:利用梯度对对比正负样本加权的。个人觉得和与正负样本加权没有区别,读完之后不想做笔记了。...
ubutun安装ffmpeg
安装依赖 sudo apt-get install yasm sudo apt-get install libsdl1.2-dev sudo apt-get install libsdl2-dev 下载安装 tar -zxvf filename.gz ./configure --enable-shared --prefix/usr/local/ffmpeg make -j4 sudo make install 添加路径 路径/usr/local/ffmpeg…...
Vue的基本用法及模板语法
Vue.js使用了基于 HTML 的模板语法,允许开发者声明式地将 DOM 绑定至底层 Vue实例的数据。所有 Vue.js的模板都是合法的 HTML,所以能被遵循规范的浏览器和 HTML 解析器解析。 在底层的实现上,Vue将模板编译成虚拟 DOM 渲染函数。结合响应系…...
Redis数据库与GO完结篇:redis操作总结与GO使用redis
一、redis操作总结 由于写redis命令的时候有提示符,所以下表只给出命令名称 数据类型操作简介字符串GET, SET, MGET, MSET, SETEX,DEL最基本的数据类型,存储任意二进制数据,支持简单操作和原子计数。适合存储重复数据。哈希HSET, HGET, HDE…...
《重生到现代之从零开始的C语言生活》—— 动态内存管理
动态内存分配 我们在开辟内存的时候就是 int a 3;这样 但是这样开的空间大小是固定的,且大小不能调整 但是如果我们用动态内存开辟的话,就可以自己申请和释放空间、 malloc 是C语言提供的一个开辟动态空间的函数 void* malloc (size_t size);//si…...
四、Spring Boot集成Spring Security之登录登出业务逻辑
Spring Boot集成Spring Security之登录登出业务逻辑 一、概要说明二、基于内存的用户名密码1、默认用户名密码2、自定义用户名密码3、为方便测试添加测试接口TestController 三、登录登出重要概念介绍四、登录业务逻辑1、登录业务相关过滤器2、访问业务请求处理流程①、访问业务…...
pipe和pipefd
Linux 中 pipe 的详细介绍 在 Linux 中,pipe 是一个系统调用,用于创建一个管道,这是一种用于进程间通信(IPC)的机制。管道允许两个进程之间进行单向数据传输,通常是一个进程向管道写入数据,而另…...
无人机之飞控仿真技术篇
一、无人机飞控仿真技术的定义 无人机飞控仿真技术主要是指飞行控制系统仿真,它是以无人机的运动情况为研究对象,面向对象的复杂系统仿真。通过该技术,可以模拟无人机的飞行过程,评估飞行控制系统的性能,优化飞行参数&…...
Tetra Pak利乐触摸屏维修beijer北尔触摸屏维修E1151
TetraPak利乐包装机触摸显示屏维修,北尔全系列型号触摸屏修理 维修注意事项: 上电前,应检查负载是否接上或是否正确; 测量电压时,确认档位是否在电压档。要确认仪器仪表的量程应大于测试点的电压; 更换电…...
Python_网络编程(IP 端口 协议)
网络编程: 互联网时代,现在基本上所有的程序都是网络程序,很少有单机版的程序了。网络编程就是如何在程序中实现两台计算机的通信。Python语言中,提供了大量的内置模块和第三方模块用于支持各种网络访问,而且Python语言…...
Adobe Acrobat提示“3D数据解析错误”
原因:在使用Adobe Acrobat打开3D PDF时,因当前Adobe Acrobat的配置存在错误,所以无法打开 解决方法:重新生成配置 首先到达下面的路径C:\Users\你的用户名\AppData\Local\Adobe\Acrobat 下面为我的路径内容 若该路径下存在文件…...
红帽7—Mysql路由部署
MySQL Router 是一个对应用程序透明的InnoDB Cluster连接路由服务,提供负载均衡、应用连接故障转移和客户端路 由。 利用路由器的连接路由特性,用户可以编写应用程序来连接到路由器,并令路由器使用相应的路由策略 来处理连接,使其…...
LLM4Rec最新工作: 字节发布用于序列推荐的分层大模型HLLM
前几个月 Meta HSTU 点燃各大厂商对 LLM4Rec 的热情,一时间,探索推荐领域的 Scaling Law、实现推荐的 ChatGPT 时刻、取代传统推荐模型等一系列话题让人兴奋,然而理想有多丰满,现实就有多骨感,尚未有业界公开真正复刻 …...
怎么高效对接SaaS平台数据?
SaaS平台数据对接是指将一个或多个SaaS平台中的数据集成到其他应用或平台中的过程。在当前的数字化时代,企业越来越倾向于使用SaaS平台来管理他们的业务和数据。然而,这些数据通常散布在不同的SaaS平台中,这对于企业数据的整合和分析来说可能…...
Spark算子使用-Map,FlatMap,Filter,diatinct,groupBy,sortBy
目录 Map算子使用 FlatMap算子使用 Filter算子使用-数据过滤 Distinct算子使用-数据去重 groupBy算子使用-数据分组 sortBy算子使用-数据排序 Map算子使用 # map算子主要使用长场景,一个转化rdd中每个元素的数据类型,拼接rdd中的元素数据…...
CSS响应式布局
CSS 响应式布局也称自适应布局,是 Ethan Marcotte 在 2010 年 5 月份提出的一个概念,简单来讲就是一个网站能够兼容多个不同的终端(设备),而不是为每个终端做一个特定的版本。这个概念是为解决移动端浏览网页而诞生的。…...
AI大模型书籍丨掌握 LLM 和 RAG 技术,这本大模型小鸟书值得一看!
本指南旨在帮助数据科学家、机器学习工程师和机器学习/AI 架构师探索信息检索与 LLMs 的集成及其相互增强。特别聚焦于 LLM 和检索增强生成(RAG)技术在信息检索中的应用,通过引入外部数据库与 LLMs 的结合,提高检索系统的性能。 …...
Mysql和Oracle使用差异和主观感受
这两种常用的关系型数据库有何差异? 支持和社区 MySQL:有一个活跃的开源社区,用户可以获取大量的文档和支持。 Oracle:提供了专业的技术支持,但通常需要额外的费用。 易用性 MySQL:通常被认为是更易于学…...
【Java】—— File类与IO流:File类的实例化与常用方法
目录 1. java.io.File类的使用 1.1 概述 1.2 构造器 1.3 常用方法 1、获取文件和目录基本信息 2、列出目录的下一级 3、File类的重命名功能 4、判断功能的方法 5、创建、删除功能 1.4 练习 练习1: 练习2: 练习3: 1. java.io.Fil…...
智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...
Spring Boot 实现流式响应(兼容 2.7.x)
在实际开发中,我们可能会遇到一些流式数据处理的场景,比如接收来自上游接口的 Server-Sent Events(SSE) 或 流式 JSON 内容,并将其原样中转给前端页面或客户端。这种情况下,传统的 RestTemplate 缓存机制会…...
ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...
零基础设计模式——行为型模式 - 责任链模式
第四部分:行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习!行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想:使多个对象都有机会处…...
MySQL中【正则表达式】用法
MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现(两者等价),用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例: 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...
用docker来安装部署freeswitch记录
今天刚才测试一个callcenter的项目,所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2
每日一言 今天的每一份坚持,都是在为未来积攒底气。 案例:OLED显示一个A 这边观察到一个点,怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 : 如果代码里信号切换太快(比如 SDA 刚变,SCL 立刻变&#…...
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的“no matching...“系列算法协商失败问题
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的"no matching..."系列算法协商失败问题 摘要: 近期,在使用较新版本的OpenSSH客户端连接老旧SSH服务器时,会遇到 "no matching key exchange method found", "n…...
Ubuntu Cursor升级成v1.0
0. 当前版本低 使用当前 Cursor v0.50时 GitHub Copilot Chat 打不开,快捷键也不好用,当看到 Cursor 升级后,还是蛮高兴的 1. 下载 Cursor 下载地址:https://www.cursor.com/cn/downloads 点击下载 Linux (x64) ,…...
