React useState状态更新异常:为何countMoves无法正确递增?
首先明确:你认为的第一种写法本身是符合React规范的,不生效是因为React状态更新的异步特性;而后面两种看似生效的写法,其实是错误的操作,只是因为直接篡改了原状态才出现“生效”的假象。
为什么第一种写法可能不生效?
React在合成事件(比如点击事件)中会对setState进行异步批量更新。当你写setState({...state, countMoves: state.countMoves + 1})时,这里的state是当前函数执行时的状态快照,并非实时最新状态。如果在同一个事件处理函数里多次调用setState,或者之前有其他修改状态的操作,你拿到的state.countMoves可能还是更新前的旧值,导致最终的更新不符合预期。
为什么后面两种写法看似生效,但其实是错误的?
state.countMoves++和state.countMoves += 1都是直接修改原state对象的操作,这严重违反了React状态不可变的核心原则。useState返回的state是不允许被直接修改的,React依赖状态的引用变化来触发组件重新渲染。你直接修改原对象后,再通过{...state}展开设置新状态,此时原state已经被篡改,组件渲染时读取的是被修改后的状态,所以看起来countMoves增加了,但这种写法会引发状态不一致、渲染异常等隐藏问题,绝对不能使用。
正确的写法
应该使用函数式更新,给setState传入一个函数,这个函数会接收上一次的状态作为参数,确保拿到的是最新的状态值:
setState(prevState => ({ ...prevState, countMoves: prevState.countMoves + 1 }))
这种写法不管setState是异步还是批量更新,都能保证每次取到的是最新的前一次状态,既符合React的状态管理规范,也能确保countMoves正确累加。
内容的提问来源于stack exchange,提问作者Arman Ebrahimi

