React17中useState在setTimeout内调用为何无法批量更新?
React 17 两种更新行为的核心原因
这个差异完全来自React 17的批量更新机制的触发边界限制:
- 第一个按钮的点击回调是React内部管理的合成事件上下文:
React会默认给所有自身管控的合成事件、生命周期回调开启批量更新策略。在这类同步执行的回调里,所有setState/useState的set调用都会被临时缓存,直到整个回调执行完毕后才会统一计算最终状态、触发一次组件重渲染。所以你调用两次set只会触发一次render,对应你看到的第一组输出。 - 第二个按钮的
setTimeout回调脱离了React的管控上下文:
React 17的批量更新仅在自身控制的执行上下文中生效,浏览器原生异步API(setTimeout、setInterval、原生事件监听、Promise回调等)的执行上下文不在React的管控范围内,因此这类回调里的每一次set调用都会立刻触发重渲染,不会做批量合并,就出现了两次render的输出。
补充说明
这个限制在React 18中已经被移除,不管是不是React管控的上下文,所有同步执行的状态更新都会默认自动批量合并,你这个案例在React 18中两个按钮的表现会完全一致。
如果要在React 17的异步上下文中实现批量更新,可以引入react-dom的unstable_batchedUpdates方法,把两次set调用包裹在这个方法的回调里,就能合并为一次渲染:
import { unstable_batchedUpdates } from 'react-dom'; // 改写后的increase方法 const increase = useCallback(() => { unstable_batchedUpdates(() => { setA(a + 1); setB(b + 1); }) }, [a, b])
内容的提问来源于stack exchange,提问作者Štěpán Stenchlák
相关产品推荐
相关产品推荐

