setState运行原理了解、及16/18不同版本情况下对比

711 阅读4分钟

目前看最新官方文档,setState存在的位置已经在 过时的React API内部了,但是类组件还是有在存在使用的,

this.setState([partialState], [callback])

partialState:支持部分状态更改

this.setState({ x:100 //不论总共有多少状态,我们只修改了x,其余的状态不动 }); callback:在状态更改/视图更新完毕后触发执行「也可以说只要执行了setState,callback一定会执行」

  • 发生在componentDidUpdate周期函数之后「DidUpdate会在任何状态更改后都触发执行;而回调函数方式,可以在指定状态更新后处理一些事情;」
  • 特殊:即便我们基于shouldComponentUpdate阻止了状态/视图的更新,DidUpdate周期函数肯定不会执行了,但是我们设置的这个callback回调函数依然会被触发执行!!
  • 类似于Vue框架中的$nextTick!!

setState是异步操作

在React18中,setState探作都是异步的「不论是在哪执行,例如:合成事件、周期函数、定时器。」

目的:
1.实现状态的批处理「统一处理」
2.有效减少更新次数,降低性能消耗
3.有效管理代码执行的逻辑顺序

原理:利用了更新队列 「updater」机制来处理的

  • 在当前相同的时间段内「浏览器此时可以处理的事情中」,遇到setstate会立即放入到更新队列中!
  • 此时状态/视图还未更新
  • 当所有的代码操作结束,会“刷新队列”「通知更新队列中的任务执行」:把所有放入setState合井在一起执行,只触发一次视图更新「批处理操作」

图片.png

从图上可以看到:

1.在事件内更新数据,可以看到是异步的更新
2.多个状态值更新时,render只会渲染一次

发现组件更新流程: shouleUpdate   --->    willUpdate ----> 修改状态值 --->   render  ---->    didUpdate   ----->callback(setState内的第二个参数)

在上图中,发现是先执行了其他打印内容,然后获取到callback内的值,就具体和下图的更新流程有关系了,callback在组件componentDidUpdate执行完成后才执行

获取更新后的值

setState的第二个参数callback会执行,在更新后执行拿到值

this.setState({
    num: num+1,
    count: count+1
  }, () => {
    console.log("num1", this.state.num)  // 1
  });

处理机制

异步操作【内容是视频截图】

图片.png

内部有更新队列,setState设置的值,不会立即更新 会先添加到更新队列,然后进行批处理操作,数据处理完成后,运行更新操作

同步操作

当前上下文执行的时候不考虑异步操作,不会等待定时器

图片.png

当前上下文执行过程中,同步代码至上而下执行完成后,把所有setState 放在更新队列当中,同步代码执行完成后,会通知队列执行setState运行

图片.png

总结

React18中,对于setState的操作,采用了 批处理

  • 构建了队列机制
  • 统一更新,提高视图更新的性能
  • 处理流程更加稳健

在React 18之前,我们只在 React合成事件/周期函数期间批量更新;默认情况下,React中不会对 promise、setTimeout、原生事件处理(native event handlers)或其它React默认不进行批处理的事件进行批处理操作!

16/ 18版本对比

React 16

图片.png

运行结果:

图片.png

这里可以看到数据 z 在setTimeout内的数据由原来的 0 变为 1,内部的setState的数据即时更新

React 18

图片.png

图片.png

这里可以看到数据 z 在setTimeout内的数据没有任何变化,内部的setState的数据是异步操作

结论: 在React18 和 React16中,关于setState是同步还是异步,是有一些区别的!

React18中:不论在什么地方执行setState,它都是异步的「都是基于updater更新队列机制,实现的批处理」

React16中:如果在合成事件「jsx元素中基于onXxx绑定的事件」、周期函数中,setState的操作是异步的!!但是如果setState出现在其他异步操作中「例如:定时器、手动获取DOM元素做的事件绑定等」,它将变为同步的操作「立即更新状态和让视图渲染」!!

获取state更新前的值

上面的是通过callback获取更新后的值,现在是获取更新前的值 setState接收的参数还可以是一个函数,在这个函数中可以拿先前的状态,并通过这个函数的返回值得到下一个状态

this.setState((prevState)=>{
    // prevState:存储之前的状态值
    // return的对象,就是我们想要修改的新状态值「支持修改部分状态」
    return {
        xxx:xxx
    };
 })