 —— 声明响应式变量(2026最新版)含useState 的异步更新机制)
类似 vue 的 data 选项功能向组件添加响应式变量当响应式变量变化时组件的视图UI也会跟着变化【数据驱动视图】语法useState函数的参数为响应式变量的初始值useState函数的返回值为一个只有两个元素的数组第一项元素为响应式变量第二项元素是一个setter 函数专门用于修改响应式变量的值setter 函数的参数是响应式变量改变前的值import{useState}fromreact;functionIndex(){const[count,setCount]useState(0);return(button onClick{()setCount((oldValue)oldValue1)}{count}/button);}exportdefaultIndex;响应式变量import{useState}fromreact;constDemo(){const[count,setCount]useState(0);functionaddOne(){setCount(count1);}returnbutton onClick{addOne}{count}/button;};exportdefaultDemo;声明了响应式变量 count 初始值为 0通过 [] 进行了数组的解构赋值将 0 赋值给了 count 可响应式改变 count 值的 setter 函数赋值给了通过 setCount 可修改 count 的值 (setCount 可以自定义为其他名称如 updateCount , 但推荐统一 set 开头)setCount 的语法是将 count 的新值作为参数传入setCount 的作用是触发视图根据 count 的新值重新渲染响应式对象const[person,setPerson]useState({firstName:Barbara,lastName:Hepworth,email:bhepworthsculpture.com});functionhandleFirstNameChange(e){// 修改属性值setPerson({...person,firstName:e.target.value});}修改嵌套的属性值const[person,setPerson]useState({name:Niki de Saint Phalle,artwork:{title:Blue Nana,city:Hamburg,image:https://i.imgur.com/Sd1AgUOm.jpg,}});functionhandleNameChange(e){setPerson({...person,name:e.target.value});}functionhandleTitleChange(e){setPerson({...person,artwork:{...person.artwork,title:e.target.value}});}响应式数组const[fruitList,setFruitList]useState([]);functionchangeHandler(e){letnewValuee.target.value;if(fruitList.includes(newValue)){// 数组删除元素setFruitList(fruitList.filter((item)item!newValue));}else{// 数组新增元素setFruitList([...fruitList,newValue]);}}实战用法 use-immer因 useState 的原生用法过于复杂实战更推荐使用第三方库 use-immernpm i use-immer更新数组const[list,setList]useImmer([1,2,3]);// 新增setList(draft{draft.push(4)});// 删除setList(draft{draft.splice(1,1)});// 修改setList(draft{draft[0]100});更新对象import{useImmer}fromuse-immer;functionApp(){// 定义状态和 useState 一样const[form,setForm]useImmer({name:张三,age:20,address:{city:北京}});// 直接修改嵌套数据无需 ... 展开constchangeCity(){setForm(draft{// draft 是一个“草稿”可以直接改draft.address.city上海;draft.age21;});};return(divp{form.name}/pp{form.address.city}/pbutton onClick{changeCity}修改城市/button/div);}实战范例–表单const[form,setForm]useImmer({username:,password:});consthandleChange(e){const{name,value}e.target;setForm(draft{draft[name]value;});};不可变原则在 react 的设计中使用 useState 声明的响应式变量是不可变的即永远不应该直接修改响应式变量本身而是将其替换成一个新值。因为调用 setState 时React 不会对比数组 / 对象里的内容只对比内存地址引用地址变了 → 判定状态更新 → 重新渲染组件地址没变 → 判定状态没更新 → 不渲染UI 不变化字符串、数字等值类型数据直接改会生成新值数组、对象是引用类型直接修改push、pop、splice、直接改属性不会改变内存地址React 认为 “没变化”。所以操作数组/对象时要使用 扩展运算符 … 等生成新副本的方式不能直接修改原值。之所以这样设计的原因是性能优化只对比引用不深度遍历内容速度极快。可追踪状态变化不会出现 “悄悄改了状态但不知道哪里改的”。时间旅行调试保留每一次状态快照方便回溯排查 bugRedux/React DevTools 依赖这个异步更新机制通过 setter 函数更新响应式变量的过程是异步的import{useState}fromreact;exportdefaultfunctionFather(){const[count,setCount]useState(10);functionincrease(){setCount(count1);console.log(count的值为:,count);}return(divp{count}/pbutton onClick{increase}/button/div);}点击按钮后的执行结果count的值为:10可见在用 setter 函数更新响应式变量后无法立马获取到其最新的值useState 的异步更新机制调用 setter 函数更新响应式变量后React 不会立即更新响应式变量的值而是将更新请求放入一个队列中。在本次事件循环结束时React 会批量处理队列中的所有响应式变量更新并触发重新渲染。为什么要异步更新而不是同步更新 1. 可显著提高性能React 可以在一次渲染过程中合并和批处理多个状态更新减少不必要的重复计算和渲染操作。2. 避免死循环若采用同步更新则每次更新状态会触发重新渲染而重新渲染又可能触发新一轮的状态更新形成死循环。连续的更新会被合并import{useState}fromreact;exportdefaultfunctionFather(){const[count, setcount]useState(10);functionincrease(){setcount(count 2);setcount(count 1);}return(divp{count}/pbuttononClick{increase}/button/div);}点击按钮后页面显示的 count 值为 11正如异步更新机制的描述多个状态更新会被合并仅最后一次更新生效怎样“及时”获取到更新后的值import{useState}fromreact;exportdefaultfunctionFather(){const[count,setcount]useState(10);functionincrease(){setcount(count1);setcount((count){console.log(setcount传入的参数count的值为:,count);returncount1;});}return(divp{count}/pbutton onClick{increase}/button/div);}点击按钮后的执行结果setcount传入的参数count的值为:11此时页面的 count 的值为 12可见通过函数传参的方式可以让 setter 函数获取到更新后的变量值。同时也从形式上避免了连续的更新合并但实质上函数传参的方式会创建新的更新请求队列从而避开了同一更新请求队列中的合并。不同版本 react 的异步更新差异react 17 时在组件生命周期或React合成事件中setState是异步 在setTimeout或者原生dom事件(如 addEventListener ) 中setState是同步。从 react18 开始所有的 setState 都是异步的注意事项useState 传入的初始值只在组件挂载render时执行有效在组件re-render (更新渲染) 时不会执行useState 返回的响应式变量的值只能通过一起返回的 setter 函数改变不可变原则操作数组/对象时要使用 扩展运算符 … 等生成新副本的方式不能直接修改原值。src/page/Index/Father.jsximport{useState}fromreact;importChildfrom./Child.jsx;exportdefaultfunctionFather(){const[userInfo,setUserInfo]useState({name:朝阳,});functionchangeName(){setUserInfo({name:张三,});}return(div style{{border:1px solid,padding:10px}}h1父组件/h1p名称为{userInfo.name}/pbutton onClick{changeName}改名称/buttonChild userInfo{userInfo}//div);}src/page/Index/Child.jsximport{useState}fromreact;functionChild({userInfo}){const[name]useState(userInfo.name);return(div style{{border:1px solid,padding:10px,margin:10px}}h1子组件/h1p父组件传入的名称为{userInfo.name}/pp子组件中 useState 返回的名称为{name}/p/div);}exportdefaultChild;