React连续修改state时useEffect触发次数不符合预期是什么原因?
现象原因核心:React的状态更新批量合并机制
1. 无setTimeout时仅触发2次的原因
- 第二个useEffect第一次触发是组件初始挂载完成后的默认执行,此时依赖的
userName和userRoom都是初始空字符串。 - React在自身控制的同步执行上下文(包括同步useEffect逻辑、生命周期函数、合成事件回调等)中,会自动开启状态更新批量合并:同一上下文中多次调用
setState,React不会每次更新都触发重渲染,而是等到当前同步代码全部执行完毕后,将所有状态变更合并为一次重渲染。 - 你在第一个useEffect的同步代码中先后调用
setUserName和setUserRoom,两次状态更新被合并为一次重渲染,两个依赖同时变更,只会触发第二个useEffect执行第二次,因此总共触发2次。
2. 加setTimeout后触发3次的原因
- 该表现符合React17及更早版本的更新规则:React的自动批量合并仅在自身管理的同步上下文生效,setTimeout属于浏览器宏任务,回调执行时已经脱离了React的同步上下文,此时批量合并规则失效。
- 首先同步执行的
setUserName(name)会单独触发一次重渲染,userName变更触发第二个useEffect执行第二次。 - 后续setTimeout回调执行时,
setUserRoom(room)会再次单独触发一次重渲染,userRoom变更触发第二个useEffect执行第三次,加上初始挂载的第一次,总共触发3次。
补充说明:如果使用React18及以上版本,默认开启了全场景自动批量更新,就算将
setUserRoom写在setTimeout回调中,两次状态更新也会被合并,依然仅触发2次第二个useEffect。如果需要主动退出批量更新,可以使用flushSync包裹更新逻辑。
内容的提问来源于stack exchange,提问作者Nisha Dave
相关产品推荐
相关产品推荐

