为什么多次调用函数式setState时回调仅打印最终状态值?
核心底层逻辑
1. React的默认批量更新机制
在React 17及更早版本中,所有由React内部管控的同步执行上下文(比如合成事件回调、组件生命周期函数)内的setState调用,不会立刻触发状态更新和组件重渲染,而是会被统一暂存到更新队列中。等整个上层回调函数完全执行结束后,React才会合并队列里的所有更新,一次性计算出最终状态,仅触发一次重渲染和componentDidUpdate生命周期。
示例中点击Increment按钮触发的onClick属于React管控的合成事件,内部连续两次调用increment的两次setState都被归入了同一次批量更新流程,所以整个过程只会走一次状态计算、一次重渲染。
2. setState回调的触发时机
setState的第二个参数回调函数的触发规则是:等待当前setState对应的更新被正式提交到组件状态、且组件完成重渲染后才会执行,而非setState调用时立刻执行。
因为两次setState被合并到了同一次批量更新中,所以两个回调都会等到批量更新完成、状态已经被计算为最终值2之后才会依次触发,此时读取this.state.count拿到的都是最终的2,不会出现中间状态1。
补充说明:示例中使用的函数式
setState(prevState => ...)已经保证了两次更新的计算是基于前一次的最新状态,所以最终count值是正确的2,只是批量更新的机制合并了渲染流程,导致回调统一在最终状态生成后执行。
实现预期输出的可选方案(非特殊场景不推荐生产使用)
如果强制需要每次setState都立刻触发更新、回调拿到中间状态,可以使用ReactDOM.flushSync包裹每次setState调用,强制跳过批量更新逻辑:
import { flushSync } from 'react-dom'; increment() { flushSync(() => { this.setState(prevState => ({ count: prevState.count + 1 }), () => { console.log('callback ' + this.state.count) }); }) console.log('not a callback ' + this.state.count); }
修改后会触发两次重渲染,回调分别拿到1和2,但会带来额外的渲染性能损耗,非必要场景不建议使用。
内容的提问来源于stack exchange,提问作者Snm Jpa

