一、 React setState 调用之后发生了什么?是同步还是异步?

  1. React setState 调用之后发生了什么
  • 在代码中调用setState函数之后,React 会将传入的参数对象与组件当前的状态合并,然后触发调和过程(Reconciliation)。经过调和过程,React 会以相对高效的方式根据新的状态构建 React 元素树并且着手重新渲染整个UI界面。
  • 在 React 得到元素树之后,React 会自动计算出新的树与老树的节点差异,然后根据差异对界面进行最小化重渲染。在差异计算算法中,React 能够相对精确地知道哪些位置发生了改变以及应该如何改变,这就保证了按需更新,而不是全部重新渲染。
  • 如果在短时间内频繁setState。React会将state的改变压入栈中,在合适的时机,批量更新state和视图,达到提高性能的效果。
  1. React setState是同步还是异步

(1)先从反例出发思考这个问题

  • 假设所有的setState都是同步的,意味着每次执行setState(有可能一个同步代码中,多次setState),都重新进行虚拟节点的比较和DOM的修改(Vnode diff + DOM), 这对性能来说是极其不友好的。
  • 如果是异步,可以把一个同步代码中的多次setState合并成一次组件更新。
  • 所以setState默认情况下是异步的,在某些场景是同步的。

(2)源码是如何实现的。

  • setState 并不是单纯的同步或者异步,它的场景会因场景的不同而不同。
  • 在源码中,它是通过isBatchingUpdates来判断setState是先存进队列还是直接更新。
  • 如果为true则执行异步操作,如果为false则直接更新。

    (3) 场景划分

  • 异步: 在react的可控范围内都是异步,如react生命周期以及合成事件中的setState,都会走合并操作,延迟更新的策略.

  • 同步: 在react不可控的范围内都是同步,如react的原生事件以及setTimeout,setInterval中的setState.

(4)从开发代码层面考虑,设计为异步的目的
一般来说, 设计为异步主要是为了减少渲染,提高性能。

  • setState设计为异步,显著提升了性能 。如果每次调用setState都进行一次更新,意味着每次调用都会执行代码里render函数,然后页面就会重新渲染,这样效率会很低下。最好的方法获取到多个更新,之后进行批量更新。
  • 如果setState同步更新了,但是代码没执行到render函数,那么就会造成state和props的不一致。state和props不能保持一致性的话,在开发过程中会产生很多问题。

二、React中的setState批量更新的过程是什么?

  • 调用setState时,组件的state并不会立即发生改变。setState只是把要修改state放入一个队列,React会优化其真正的执行时机。
  • 出于性能考虑, React会将生命周期和合成事件中的多次setState的状态修改合并一次状态修改,最终更新就会只产生一次组件及其子组件的重新渲染,这对于大型应用程序中的性能提升至关重要。
  • 需要注意的是,只要同步代码还在执行,“攒起来”这个动作就不会停止。(注:这里之所以多次 +1 最终只有一次生效,是因为在同一个方法中多次 setState 的合并动作不是单纯地将更新累加。比如这里对于相同属性的设置,React 只会为其保留最后一次的更新)。
    1. this.setState({
    2. count: this.state.count + 1 ===> 入队,[count+1的任务]
    3. });
    4. this.setState({
    5. count: this.state.count + 1 ===> 入队,[count+1的任务,count+1的任务]
    6. });
    7. 合并 state,[count+1的任务]
    8. 执行 count+1的任务