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…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...
[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解
突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 安全措施依赖问题 GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...
Java 语言特性(面试系列2)
一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...
 
springboot 百货中心供应链管理系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,百货中心供应链管理系统被用户普遍使用,为方…...
 
Day131 | 灵神 | 回溯算法 | 子集型 子集
Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣(LeetCode) 思路: 笔者写过很多次这道题了,不想写题解了,大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...
【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密
在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...
 
CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...
 
LeetCode - 394. 字符串解码
题目 394. 字符串解码 - 力扣(LeetCode) 思路 使用两个栈:一个存储重复次数,一个存储字符串 遍历输入字符串: 数字处理:遇到数字时,累积计算重复次数左括号处理:保存当前状态&a…...
【无标题】路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论
路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论 一、传统路径模型的根本缺陷 在经典正方形路径问题中(图1): mermaid graph LR A((A)) --- B((B)) B --- C((C)) C --- D((D)) D --- A A -.- C[无直接路径] B -…...
 
C/C++ 中附加包含目录、附加库目录与附加依赖项详解
在 C/C 编程的编译和链接过程中,附加包含目录、附加库目录和附加依赖项是三个至关重要的设置,它们相互配合,确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中,这些概念容易让人混淆,但深入理解它们的作用和联…...
