为什么useState的set函数在异步场景下会立即生效而非批量更新?
React useState异步更新多次渲染问题解答
这是React的预期行为
该现象由React的状态更新批处理机制导致:
在React 17及更早版本中,默认仅对React可控场景内的状态更新做批处理,包括合成事件回调、组件生命周期函数等。你测试的同步点击属于React合成事件触发的回调,内部多次set调用会被合并为一次重渲染,所以只会触发一次组件render。
而setTimeout、Promise.then、原生事件监听等场景的执行上下文在React的控制之外,旧版本React不会对这些场景的状态更新做自动批处理,每调用一次set函数就会立刻触发一次重渲染,就出现了你看到的调用两次set触发两次render的表现。
如果使用React 18及以上版本,默认已经开启了全场景自动批处理,不管是同步还是异步回调中的多次状态更新都会自动合并,不会出现两次渲染的问题。
异步场景实现同步一致表现的方案
- 方案1:升级到React 18及以上版本
这是成本最低的方案,升级后无需修改业务代码,异步场景下的多次状态更新也会自动批处理,和同步场景表现完全一致。 - 方案2:使用手动批处理API(React 17及更早版本适用)
如果无法升级版本,可以使用ReactDOM提供的unstable_batchedUpdates方法包裹异步场景的更新逻辑,手动开启批处理:
import { unstable_batchedUpdates } from 'react-dom'; // 修改异步按钮的点击逻辑 <button onClick={() => { console.log('--------------async click--------------'); setTimeout(() => { unstable_batchedUpdates(updateState); }, 0) }}>Update State a & b Async</button>
包裹后内部的两次set调用会被合并为一次重渲染。
- 方案3:合并关联状态
如果多个状态本身是强关联的,可以将其合并为单个对象类型的状态,一次更新即可修改所有值,天然只会触发一次重渲染:
const [state, setState] = useState({a: 1, b: 11}); const updateState = () => { console.log('\tupdating starts.'); setState(prev => ({...prev, a: 2, b: 12})); console.log('\tupdating ends.'); };
内容的提问来源于stack exchange,提问作者MRNafisiA
相关产品推荐
相关产品推荐

