为什么不能直接改 state,为什么 setter 后读到旧值
先理解原理和示例,再练习完整口述与追问。
回答要点
只读快照;新引用;当前闭包不会被改写。
原理拆解
每次函数组件渲染都取得那一次的 state 快照,事件处理函数闭包也关联这份快照。setter 是向 React 提交后续状态更新,并不会改写当前函数里的局部变量。后续渲染会得到新值,当前回调即使又等待了一会儿,也仍然可能引用旧渲染的变量。是否批处理影响何时渲染,但不是快照语义的唯一原因。
示例与推演
function Counter() {
const [count, setCount] = useState(0)
function click() {
setCount(count + 1)
console.log(count) // 本次点击仍是当前渲染快照
}
return <button onClick={click}>{count}</button>
}
若下一次计算依赖前一状态,用 updater;若要在新值提交后同步外部系统,使用合适的 Effect,而不是直接读取 setter 返回值。
易错点
函数组件 useState 不会像类 setState 那样自动浅合并对象。不要用 setTimeout 等一会儿来“修复”闭包,也不要认为 await setter 可以等到新渲染完成。
参考回答可直接练习口述
React 的 state 对当前渲染是只读快照。修改对象本身会破坏快照且可能因引用不变跳过更新,应复制实际变化路径后调用 setter。setter 请求下一次渲染,不会把当前函数里的变量立即改成新值,所以同一回调里仍读旧快照。