react setState 异步原理与更新策略

301 阅读2分钟

react setState 异步原理与更新策略

setState的关键点

  1. setState不会立刻改变React组件中state的值
  2. setState通过引发一次组件的更新过程来引发重新绘制 重绘指的就是引起React的更新生命周期函数4个函数: shouldComponentUpdate(被调用时this.state没有更新;如果返回了false,生命周期被中断,虽然不调用之后的函数了,但是state仍然会被更新) componentWillUpdate(被调用时this.state没有更新) render(被调用时this.state得到更新) componentDidUpdate
  3. 多次setState函数调用产生的效果会合并。 this.setState({name: 'Pororo'}) this.setState({age: 20}) this.setState({name: 'Pororo',age: 20}) 上面两块代码的效果是一样的。如果每次调用都引发一次生命周期更新,那性能就会消耗很大了。所以,React会将多个this.setState产生的修改放进一个队列里,等差不多的时候就会引发一次生命周期更新。
class App extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      count: 0
    }
  }
  componentDidMount() {
    this.setState({ count: this.state.count + 1 })
    console.log(this.state.count) // 第一次输出
    this.setState({ count: this.state.count + 1 })
    console.log(this.state.count) // 第二次输出
    setTimeout(() => {
      this.setState({ count: this.state.count + 1 })
      console.log(this.state.count) // 第三次输出
      this.setState({ count: this.state.count + 1 })
      console.log(this.state.count) // 第四次输出
    }, 0)
  }
  render() {
    return null
  }
}
ReactDOM.render(
  <App />,
  document.getElementById('root')
);

结果:控制台打印输出为: 0, 0, 2, 3。

先来分析前两次setState:

this.setState({count: this.state.count + 1})
console.log(this.state.count) // 第一次输出
this.setState({count: this.state.count + 1})
console.log(this.state.count) // 第二次输出

由于setState不会立即改变React组件中state的值,所以两次setState中this.state.value都是同一个值0,故而,这两次输出都是0。因而count只被加1。【合并更新】 既然这样,那么是不是可以直接操作this.state呢?

比如:this.state.count=this.state.count+1;这样的确可以修改this.state.count的状态但是却不可以引发重复渲染。所以,就必须通过React设定的setState函数去改变this.state,从而引发重新渲染。 接下来分析setTimeout里面的两次setState:

setTimeout(() => {
this.setState({count: this.state.count + 1})
console.log(this.state.count) // 第三次输出
this.setState({count: this.state.count + 1})
console.log(this.state.count) // 第四次输出
}, 0)

这两次this.stat的值同步更新了,这是为什么的呢?

s14030206142022

在React中,如果是由React引发的事件处理(比如:onClick引发的事件处理),调用setState不会同步更新this.state,除此之外的setState调用会同步执行this.setState。 “除此之外”指的是:绕过React通过addEventListener直接添加的事件处理函数和setTimeout/setInterval产生的异步调用。 那为什么会这样呢?

每次setState产生新的state会依次被存入一个队列,然后会根据isBathingUpdates变量判断是直接更新this.state还是放进dirtyComponent里回头再说。isBatchingUpdates默认是false,也就表示setState会同步更新this.state。但是,当React在调用事件处理函数之前就会调用batchedUpdates,这个函数会把isBatchingUpdates修改为true,造成的后果就是由React控制的事件处理过程setState不会同步更新this.state。