React函数组件中useState变量重渲染自动更新的实现机制是什么?
先看两个对比案例:
纯JavaScript的解构行为
在纯JS中,数组解构是值拷贝,原数组修改不会影响已解构出的变量:
const arr = [1]; let [v] = arr; arr[0] = 2; console.log(v); // 输出1,v是独立变量,不受arr修改影响
React中useState的表现
但在React函数组件中,调用setter更新state后,return里的state变量会同步显示最新值:
function App() { let [cnt, setCnt] = useState(0); return ( <div className="App"> <div>Counter: {cnt}</div> <button onClick={() => {setCnt(cnt+1)}}>Inc</button> </div> ); }
核心原因:React的内存单元格机制
React为每个组件实例维护了一个内部的内存单元格列表——这些就是存储state的JavaScript对象。当组件首次渲染时:
- 调用
useState(0)会初始化第一个内存单元格,存入值0,同时返回这个值和对应的更新函数。
当你点击按钮调用setCnt(cnt+1)时:
- React会找到对应内存单元格,把值更新为1;
- 触发组件重渲染,也就是重新执行
App函数。
重渲染时再次调用useState:
- React会按照调用顺序,从对应的内存单元格中取出最新的1,赋值给新的
cnt变量; - 所以return块里的
cnt是这次渲染的最新值,自然会显示更新后的结果。
关于转译的疑问
React并没有把cnt转译成类似MAGIC_MAP["cnt"]的形式,而是完全依赖Hook调用的顺序来关联内存单元格。只要每次渲染时Hook的调用顺序不变,React就能精准找到对应state的存储位置。
和类组件的对比
类组件中我们把state存在this.state里,这是利用了JS对象引用的特性——this始终指向组件实例,每次渲染访问this.state都是当前实例的最新状态。而函数组件的useState则是通过内存单元格+重渲染时重新执行函数,让每次渲染都能拿到最新的state值,本质是每次渲染都会创建新的cnt变量,但它的值来自React维护的内存单元格。
引用React文档的说明:
我们声明了一个名为count的state变量,并将其初始化为0。React会在重渲染之间记住它的当前值,并向我们的函数提供最新值。如果我们想要更新当前count,可以调用setCount。
每个组件都关联着一个内部的“内存单元格”列表。它们只是可以存储数据的JavaScript对象。当你调用useState()这样的Hook时,它会读取当前单元格(或在首次渲染时初始化),然后将指针移到下一个单元格。这就是多个useState()调用能各自拥有独立局部state的方式。
内容的提问来源于stack exchange,提问作者gavenkoa

