首页/新闻资讯/正文详情

useState 深入浅出:React 状态管理的基石

发布时间:2026/9/28 3:40:00 来源:云帆数科 栏目:资讯中心
useState 深入浅出:React 状态管理的基石
文章目录一、useState —— 响应式数据状态1.1 什么是状态1.2 Hooks 函数式编程的带头大哥1.3 参数详解初始值 | 函数1.4 返回值[state, setState]二、Fragment 组件 —— 隐形的容器2.1 为什么需要 Fragment2.2 Fragment 怎么用三、setState 的异步调度更新3.1 异步到底是什么意思3.2 批量更新与合并机制3.3 函数式更新打破闭包陷阱四、惰性初始化 —— useState 传函数的真正用途4.1 问题的本质4.2 两种写法的天差地别4.3 执行次数验证4.4 完整示例五、全文总结5.1 核心知识点复盘5.2 常见问题 / 避坑指南一、useState —— 响应式数据状态1.1 什么是状态在聊useState之前我们先搞清楚一个概念什么是状态State简单说状态就是组件在某一时刻的数据快照。比如一个计数器当前是几、输入框里打了什么字、用户列表有多少条数据——这些都是状态。状态一旦改变React 就会自动重新渲染组件让界面与数据保持同步。这就是响应式的含义数据变了UI 自动跟着变。类比状态就像电视剧的当前帧——每一帧都是那一瞬间的画面帧变了观众看到的画面就变了。1.2 Hooks 函数式编程的带头大哥2019 年 React 16.8 正式推出 HooksuseState是第一个也是使用频率最高的 Hook。它的出现让函数组件从此拥有了自己的状态管理能力不再需要写 Class 组件。import { useState } from react; function App() { const [count, setCount] useState(0); // count当前状态值 0初始值 // setCount修改状态的函数 return ( p当前计数{count}/pbutton onClick{() setCount(count 1)}1/button/ ); }这是最精简的useState示例核心就三步引入 → 声明 → 使用。1.3 参数详解初始值 | 函数useState(参数)接收一个参数这个参数决定了状态的初始值。它有两种传法传法写法适用场景直接传值useState(0)、useState(hello)初始值简单、计算成本低传函数useState(() 计算初始值)初始值需要复杂计算详见第四章先看直接传值const [name, setName] useState(张三); // 字符串 const [age, setAge] useState(25); // 数字 const [list, setList] useState([1, 2, 3]); // 数组 const [user, setUser] useState({ id: 1 }); // 对象再看传函数惰性初始化// ❌ 错误做法每次组件渲染都会执行 heavyComputation() const [users] useState(heavyComputation()); // ✅ 正确做法只在组件首次挂载时执行一次 const [users] useState(() heavyComputation());这个区别非常关键第四章会展开讲。现在你只需要记住如果初始值需要算出来就传函数如果初始值就是一个现成的值就传值。1.4 返回值[state, setState]useState返回一个长度为 2 的数组我们用解构赋值来接收const [count, setCount] useState(0);返回值是什么能做什么第一个元素count当前状态的值在 JSX 中渲染、传给子组件、参与计算第二个元素setCount修改状态的函数调用它 → 状态更新 → 组件重新渲染核心规则铁律永远不要直接修改 statecount 5是无效的必须用setCount(5)。状态是只读的React 通过setState函数接管了修改权这样才能在合适的时机触发重新渲染。state 和 setState 的命名约定[something, setSomething]这是社区约定不是语法要求但强烈建议遵守。二、Fragment 组件 —— 隐形的容器2.1 为什么需要 FragmentReact 组件的return语句有一个硬性规则必须返回单个根元素。以前我们习惯用div来包一层但这样会在 DOM 中多出一个无意义的节点。// ❌ 不能这样写——多个根元素会报错 return ( p第一段/p p第二段/p ); // 以前的做法包一层 div return ( div p第一段/p p第二段/p /div );多出的div会污染 DOM 结构影响 CSS 布局尤其是 Flexbox / Grid还会让语义变得不清晰。2.2 Fragment 怎么用Fragment就是一个不会在 DOM 中生成任何真实节点的虚拟容器import{Fragment}fromreact;functionApp(){return(Fragmentp第一段/pp第二段/p/Fragment);}或者用更简洁的简写语法空标签functionApp(){return(p第一段/pp第二段/p/);}两种写法效果完全一样。最终渲染到#root后页面上只有两个p标签Fragment 本身功成身退——它完成了挂载子元素的使命自己不会留下任何痕迹。React Fragment 的灵感来源浏览器原生DocumentFragment其实 React 的 Fragment 并非凭空发明浏览器原生就提供了类似的机制——document.createDocumentFragment()也叫文档碎片。它同样是一个没有实体、只存在于内存中的虚拟容器用于批量挂载 DOM 元素避免多次回流reflow带来的性能损耗ulidlist/ulscriptconstdata[任务1,任务2,任务3];constoListdocument.querySelector(#list);// 创建文档碎片——和 React 的 / 一样没有实体constfragmentdocument.createDocumentFragment();for(consttaskofdata){constitemdocument.createElement(li);// 在 JS 内存中创建item.innerTexttask;fragment.appendChild(item);// 先挂到碎片上不会触发页面渲染}// 所有 li 一次性挂到页面上只触发一次回流oList.appendChild(fragment);// fragment 自己不会留在 DOM 中/script对比理解ReactFragment/.../原生document.createDocumentFragment()本质虚拟容器不生成真实 DOM 节点内存中的临时容器不生成真实 DOM 节点作用满足单根元素规则不污染 DOM批量操作 DOM减少回流次数最终去向渲染后功成身退子元素直接挂到父节点appendChild后自身消失子元素挂到目标节点两个Fragment的设计思想一脉相承用一个透明的容器组织一批子元素任务完成后自己消失只留下子元素。注意简写/不能带 key 属性如果你在列表渲染中需要用 key必须写完整的Fragment key{...}。三、setState 的异步调度更新这是useState最容易踩坑的地方也是 React 状态管理的核心设计思想。3.1 异步到底是什么意思看这段代码猜猜控制台打印什么function App() { const [count, setCount] useState(0); const addCount () { setCount(count 1); console.log(count); // 你猜这里打印什么 }; return ( p当前计数{count}/pbuttononClick{addCount}1/button/ ); }答案打印0不是1。原因setCount调用后React不会立刻修改count的值。它只是把更新这件事排进一个任务队列等当前代码全部执行完毕后才会批量处理这些更新然后重新运行组件函数此时count才能拿到新值。用时间线来看用户点击按钮 ↓ addCount 开始执行 ↓ setCount(count 1) → React好的我记下了等会统一处理 ↓ console.log(count) → 此时 count 仍是旧值 0 ↓ addCount 执行完毕 ↓ React开始批量更新状态... ↓ 组件重新渲染count 变为 1 ↓ 页面显示 当前计数1一句话总结setState是异步调度的调用它不会立即改变 state当前作用域内拿到的仍然是旧值。3.2 批量更新与合并机制再看一段更有意思的代码const addCount () { setCount(count 1); // count 0, 期望变成 1 setCount(count 1); // count 0, 期望变成 2 setCount(count 1); // count 0, 期望变成 3 };实际结果count 只加了 1不是 3。为什么因为三次setCount都在同一个事件处理函数中执行此时count自始至终都是0setCount(0 1) → React 记录count → 1 setCount(0 1) → React 记录count → 1 setCount(0 1) → React 记录count → 1React 发现三次更新都是把 count 设为 1于是合并为一次只渲染一次——这叫做批量更新Batching目的是性能优化。如果每次setCount都立即重新渲染页面会频繁重绘性能很差。核心机制React 18 会自动对所有事件处理函数、setTimeout、Promise 中的状态更新进行批量处理。同一轮事件循环中的多次setState会被合并。版本批量更新范围React 17 及之前仅在 React 事件处理函数中合并React 18在所有地方事件、setTimeout、Promise、原生事件都合并3.3 函数式更新打破闭包陷阱如果确实需要基于最新状态累加怎么办—— 给setState传一个函数const addCount () { setCount(prevCount prevCount 1); // prevCount 0 → 返回 1 setCount(prevCount prevCount 1); // prevCount 1 → 返回 2 setCount(prevCount prevCount 1); // prevCount 2 → 返回 3 }; // 最终 count 3 ✅函数式更新的工作原理prevCount prevCount 1是一个更新函数它接收的参数prevCount是上一次更新后的最新状态而不是闭包中捕获的旧值。React 会将更新函数放入队列按顺序依次执行每个函数都基于前一个函数的返回值来计算setCount(prev prev 1) → 0 1 1 setCount(prev prev 1) → 1 1 2 prev 已经是 1 了 setCount(prev prev 1) → 2 1 3 prev 已经是 2 了什么时候用函数式更新当新状态依赖旧状态时一律用函数式更新。比如计数器累加、数组 push、对象展开合并等场景。这能避免因闭包捕获旧值导致的 bug。两种更新方式的对比方式写法基于的值适用场景传值更新setCount(count 1)当前作用域的旧 count不依赖旧值直接设置新值函数式更新setCount(prev prev 1)React 保证的最新状态新值依赖旧值累加、追加四、惰性初始化 —— useState 传函数的真正用途回到第一章提过的问题useState的参数什么时候传值什么时候传函数4.1 问题的本质看这个场景组件需要 10000 条用户数据作为初始状态// 模拟耗时计算functionheavyComputation(){console.log(开始执行 heavyComputation...);conststartTimeperformance.now();constresult[];for(leti0;i10000;i){result.push({id:i,name:用户-${i}});}constdurationperformance.now()-startTime;console.log(heavyComputation 执行耗时${duration}ms);returnresult;}4.2 两种写法的天差地别// ❌ 错误每次渲染都会执行 heavyComputation()const[users]useState(heavyComputation());// ✅ 正确只在首次挂载时执行一次const[users]useState(()heavyComputation());为什么第一种写法是错的当你写useState(heavyComputation())时heavyComputation()会立即执行然后把返回值传给useState。这意味着每次组件渲染这行代码都会被执行heavyComputation()都会重新跑一次——哪怕 React 最终只使用第一次的返回值。而useState(() heavyComputation())传的是一个函数引用React 在内部判断这是惰性初始化模式只在组件首次挂载时调用它一次。后续组件因为状态变化重新渲染时React 发现初次初始化已经完成会直接忽略这个函数。4.3 执行次数验证打开控制台分别运行两种写法然后修改filterText触发重新渲染写法heavyComputation 执行次数控制台输出useState(heavyComputation())每次渲染都执行N 行 “开始执行…”useState(() heavyComputation())仅挂载时执行 1 次1 行 “开始执行…”区别非常明显。对于 10000 条数据尚且能忍受但如果是复杂算法、大量数据、随机逻辑生成第一种写法会让页面卡到无法使用。4.4 完整示例import{useState}fromreact;functionheavyComputation(){console.log(开始执行 heavyComputation...);conststartTimeperformance.now();constresult[];for(leti0;i10000;i){result.push({id:i,name:用户-${i}});}constdurationperformance.now()-startTime;console.log(heavyComputation 执行耗时${duration}ms);returnresult;}functionApp(){const[filterText,setFilterText]useState();// ✅ 惰性初始化传函数只在挂载时执行一次const[users]useState(()heavyComputation());// 基于 filterText 过滤用户列表// 空字符串被认为是任何字符串的子串constfilteredUsersusers.filter(useruser.name.includes(filterText));return(div style{{padding:20px}}h2用户列表/h2input typetextplaceholder输入用户名过滤value{filterText}onChange{(e)setFilterText(e.target.value)}/p当前显示{filteredUsers.length}个用户/pul style{{maxHeight:300px,overflowY:auto}}{filteredUsers.map(user(li key{user.id}{user.name}/li))}/ul/div);}exportdefaultApp;数据流理解组件首次渲染 → 执行heavyComputation生成 10000 条数据 → 存入users→ 用户在输入框打字 →filterText更新 → 组件重新渲染 →不再执行heavyComputation→filteredUsers根据新filterText重新过滤 → UI 更新。五、全文总结5.1 核心知识点复盘知识点一句话总结useState 本质让函数组件拥有自己的响应式状态数据变 → UI 自动变参数两种形态简单值直接传复杂计算传函数惰性初始化返回值结构[state, setState]数组解构命名约定驼峰Fragment虚拟容器不生成 DOM 节点简写.../setState 异步性调用后不立即改值React 统一批量调度批量更新同一事件循环中的多次 setState 会被合并减少渲染次数函数式更新setCount(prev prev 1)基于最新状态计算解决闭包问题惰性初始化useState(() 重计算)只在挂载时执行一次5.2 常见问题 / 避坑指南Q1为什么setCount后立刻console.log(count)还是旧值因为setCount是异步调度不会立即更新count。需要在新值的地方用应该放在组件渲染时打印或者用useEffect监听变化。// ❌ 错误期望 setCount(5); console.log(count); // 还是旧值 // ✅ 正确方式在渲染时打印 console.log(当前 count:, count); // 组件重新渲染时会执行这行Q2为什么连续调用三次setCount(count 1)只加了 1因为三次调用的count都是闭包中的同一个旧值。解决方式是使用函数式更新// ❌ 基于旧值 setCount(count 1); setCount(count 1); setCount(count 1); // ✅ 基于最新值 setCount(prev prev 1); setCount(prev prev 1); setCount(prev prev 1);Q3useState(heavyComputation())和useState(() heavyComputation())到底差在哪前者heavyComputation()会每次渲染都执行虽然 React 只用第一次的值后者传的是函数引用React 只在首次挂载时调用一次。当初始值计算成本高时必须用后者否则严重浪费性能。Q4对象/数组状态怎么正确更新状态是不可变的immutable必须创建新对象/新数组不能直接修改原值// ❌ 错误直接修改 user.name 李四; setUser(user); // React 发现是同一个引用不会重新渲染 // ✅ 正确创建新对象 setUser({ ...user, name: 李四 }); // ✅ 数组追加 setList([...list, newItem]); // ✅ 数组删除 setList(list.filter(item item.id ! targetId));Q5什么时候应该拆分成多个 useState什么时候合并成一个如果状态之间相互独立、更新频率也不同拆开更合适比如useState管理用户名、useState管理密码。如果状态总是一起变化放在一个对象里更方便比如表单数据{ name, age, email }。没有绝对的对错但拆得细一点通常更灵活更新时不用手动合并其余字段。最后的话useState是 React 函数组件的入口理解了它的异步调度、批量更新和惰性初始化就打通了 React 状态管理的第一关。多写代码、多打断点、多在控制台观察状态变化才能从会用到理解。

相关推荐

使用curl命令直接测试大模型接口,快速排错与验证配置
使用curl命令直接测试大模型接口,快速排错与验证配置

使用curl命令直接测试大模型接口,快速排错与验证配置 在接入大模型服务时,开发者常常需要快速验证API接口的连通性、检查配置是否正确,或者在不依赖特定编程语言SDK的环境中进行初步测试。curl作为一个功能强大的命令行工具,是完… · 2026/9/28 3:39:29

深度学习与传统视觉融合的水位监测技术实践
深度学习与传统视觉融合的水位监测技术实践

1. 项目背景与行业痛点水位监测作为水利、环保、城市管理等领域的核心需求,长期以来依赖人工观测或传统视觉识别技术。我在某水利信息化项目中首次接触到这个需求——当时客户需要724小时实时监测水库水位,但现场环境给传统方法带来三大挑战:… · 2026/7/27 0:45:25

AI营销与地理优化:云南企服科技的双轮驱动策略
AI营销与地理优化:云南企服科技的双轮驱动策略

1. 云南企服科技的业务定位与市场切入点云南企服科技有限公司作为一家扎根西南地区的企业服务提供商,其业务模式充分结合了区域经济特点和数字化转型需求。公司核心聚焦于两个具有高度协同效应的业务板块:AI驱动的营销获客解决方案和基于地理数据的商业优… · 2026/9/20 21:46:24

Understanding Driving Risks using Large Language Models: Toward Elderly Driver Assessment
Understanding Driving Risks using Large Language Models: Toward Elderly Driver Assessment

文章主要内容总结 本文研究了多模态大语言模型(具体为ChatGPT-4o)利用静态行车记录仪图像进行类人交通场景解读的潜力,重点聚焦与老年司机评估相关的三项任务:交通密度评估、交叉口可见性评估和停车标志识别。这些任务需上下文推理而非简单目标检测。研究采用零样本、少样… · 2026/9/28 3:32:43

Leveraging Large Language Models for Classifying App Users‘ Feedback
Leveraging Large Language Models for Classifying App Users‘ Feedback

文章主要内容总结 本文聚焦于利用大型语言模型(LLMs)解决应用用户反馈分类的挑战,传统方法依赖有监督机器学习,但受限于标注数据集的规模和质量。研究通过三个核心实验评估了4种先进LLMs(GPT-3.5-Turbo、GPT-4o、Flan-T5、Llama3-70b)的性能: LLMs在用户反馈分类中的基… · 2026/9/28 3:32:43

Using Large Language Models for Legal Decision-Making in Austrian Value-Added Tax Law: An Experim...
Using Large Language Models for Legal Decision-Making in Austrian Value-Added Tax Law: An Experim...

文章主要内容总结 本文通过实验评估了大型语言模型(LLMs)在奥地利及欧盟增值税(VAT)法框架下辅助法律决策的能力。研究聚焦于两种提升LLM性能的方法——微调(fine-tuning)和检索增强生成(RAG),并在两类案例中进行验证:一是权威教科书案例,二是税务咨询公司的真实案… · 2026/9/28 3:32:43

学Java别走弯路,这5个方向最吃香
学Java别走弯路,这5个方向最吃香

学Java的人很多,但学明白的人不多。有人学了半年还在写控制台程序,有人一年就能独当一面。差别不在天赋,而在方向。Java生态太庞大了,什么都学等于什么都没学。选对方向,事半功倍。今天盘点当前最吃香的5个Java方向&am… · 2026/9/28 3:32:15

AlphaAgents: Large Language Model based Multi-Agents for Equity Portfolio Constructions
AlphaAgents: Large Language Model based Multi-Agents for Equity Portfolio Constructions

AlphaAgents相关总结与翻译 一、文章主要内容总结 (一)研究背景与问题 传统股票投资组合管理依赖人类分析师处理海量信息(如财务披露、财报、市场新闻等),存在信息处理效率低、易受认知偏差(如损失厌恶、过度自信)影响的问题,可能错失投资收益机会。尽管AI在数据处理… · 2026/9/28 3:32:08

Java跑亿级报表OOM被开除?我靠达梦并行查询+虚拟线程分片拉取,3分钟出结果,DBA看懵了!
Java跑亿级报表OOM被开除?我靠达梦并行查询+虚拟线程分片拉取,3分钟出结果,DBA看懵了!

// 💥 翻车现场:经典“单线程+全量加载”的自杀式报表生成 String sql = “SELECT province, sum(amount), count(distinct user_id) FROM t_trade … GROUP BY province”; List<Map<String, Object>> result = jdbcTemplate.queryForList(sql); // 💥 5亿行… · 2026/9/28 3:32:02

MATLAB雷达信号脉冲压缩仿真:LFM线性调频、匹配滤波与距离分辨率实现
MATLAB雷达信号脉冲压缩仿真:LFM线性调频、匹配滤波与距离分辨率实现

简介&#xff1a;这套Matlab仿真工具完整呈现雷达信号脉冲压缩过程&#xff0c;从线性调频&#xff08;LFM&#xff09;信号生成、目标回波仿真到匹配滤波压缩处理均有可运行代码支撑&#xff0c;面向电子信息工程、计算机、数学等专业学生&#xff0c;适用于课程设计、期末大作… · 2026/9/27 0:00:01

汕头网站建设制作厂家避坑指南:5大注意事项救急
汕头网站建设制作厂家避坑指南:5大注意事项救急

汕头网站建设制作厂家避坑指南:5大注意事项救急 改个需求建站公司拖一周,这种憋屈事我见得太多了。 很多汕头老板找本地建站团队,签合同前看着方案挺美,一上线就变脸。 今天不聊虚的,直接拆解找 汕头网站建设制作厂家 时的5个核心 注意事项… · 2026/9/27 0:00:01

多模态虚假新闻检测实战:BERT+ResNet双塔与对比学习
多模态虚假新闻检测实战:BERT+ResNet双塔与对比学习

简介&#xff1a;基于PyTorch的多模态虚假新闻检测项目完整代码包&#xff0c;面向自然语言处理与计算机视觉交叉方向的开发者、科研人员及毕业设计选题者&#xff0c;解决社交媒体中文本与图像联合识别虚假新闻的问题。系统以BERT预训练模型提取文本语义特征&#xff0c;以Res… · 2026/9/27 0:00:01

制作网页比较方便的软件怎么选?一文搞懂避坑指南
制作网页比较方便的软件怎么选?一文搞懂避坑指南

制作网页比较方便的软件怎么选?一文搞懂避坑指南 很多老板一上来就问:做个网站多少钱?但我反问他:你的域名买了吗?服务器租了吗?他一脸懵。这就是典型的“域名服务器搞不懂”。别急,今天咱们不聊虚的,直接 一文搞懂 那些让你头秃的技术名词。… · 2026/9/28 0:00:06

婚恋网站实战案例:避开3个高价坑,省钱50%还能跑赢流量
婚恋网站实战案例:避开3个高价坑,省钱50%还能跑赢流量

婚恋网站实战案例:避开3个高价坑,省钱50%还能跑赢流量 找婚恋网站建站公司,最怕的就是被坑高价。很多同行跟我吐槽,报价单上写得模棱两可,功能栏里全是“高级定制”、“专属UI”,结果落地全是套壳。今天不聊虚的,直接甩几个我经手的 实战案例… · 2026/9/28 0:00:19

济南做网站多少钱:3个案例拆解,防黑源码下载全攻略
济南做网站多少钱:3个案例拆解,防黑源码下载全攻略

济南做网站多少钱:3个案例拆解,防黑源码下载全攻略 上周济南一个做建材的老板找我,脸都绿了。他的官网首页弹出了赌博广告,后台被植入了挖矿脚本。他慌得问我:“网站被黑挂马不知道怎么办?能不能直接找之前的外包公司要源码下载,看看哪里被动了手脚?… · 2026/9/28 0:00:25

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码