react中setState的同步/异步问题

195 阅读2分钟
  • react中setState并不是单纯的同步或异步,关于同步和异步的判断,取决于调用时所处的环境
  • 合成事件生命周期钩子中(除componentDidUpdate以外),setState是异步
    • 原则:如果当前有事务流正在执行,则组件的更新无法进行,而是会被推入dirtyComponents队列中等待事务流结束之后执行;如果当前没有事务流,则组件的更新会被直接推入到batchedUpdates队列中执行
    • 原因
      • 在生命周期钩子函数调用中,更新策略都处于更新操作之前,此时组件仍然处于事务流之中,所以无法同步更新状态;但是,componentDidUpdate钩子是在更新之后,此时组件已经不在事务流之中了,所以是同步的setState
      • 在合成事件中,react事务流是基于事件委托完成的,所以事务流是处于一个正在执行的状态,相关的setState是异步的
    • 异步setState存在的问题是:无法立刻获取到最新的state
    • 解决办法:在setState中使用第二个参数,也就是添加回调函数:setState((newState), callback(){return newState})
  • 原生事件setTimeout中,setState的实现是同步的:可以马上获取到更新后的状态
    • 原因:原生事件是浏览器本身的实现,与事务流无关,所以是同步事件。setTimeout是在定时器线程中执行的,此时事务流已经结束,所以也是同步的
  • 批量更新: 在合成事件以及除了componentDidMount生命周期钩子中,setState更新状态时,存储的是合并状态,也就意味着之前存入的key值会被之后的覆盖,最终只执行一次渲染更新
  • 函数式setState:
    • 由于Fiber以及合并的问题,react官方推荐使用函数式setState(fn),fn返回新state,也就是setState((state,props)=>newState)
    • 使用函数式setState可以避免setState批量更新的逻辑,减少计算量