State的更新可能是异步的

Thursday, April 15, 2021

this.props 和 this.state 可能是异步更新的,你不能依赖他们的值计算下一个state(状态)

  • 调用setState后,setState会把要修改的状态放入一个队列中(因而 组件的state并不会立即改变);
  • 之后React 会优化真正的执行时机,来优化性能,所以优化过程中有可能会将多个 setState 的状态修改合并为一次状态修改,因而state更新可能是异步的。
  • 所以不要依赖当前的State,计算下个State。当真正执行状态修改时,依赖的this.state并不能保证是最新的State,因为React会把多次State的修改合并成一次,这时,this.state将还是这几次State修改前的State。
  • 另外需要注意的事,同样不能依赖当前的Props计算下个状态,因为Props一般也是从父组件的State中获取,依然无法确定在组件状态更新时的值。
class App extends React.Component {
  state = {
    counter: 0
  }
  handleClick = () => {
    const counter = this.state.counter;
    this.setState({ counter: counter + 1 })
    this.setState({ counter: counter + 1 })
    this.setState({ counter: counter + 1 })
  }
  render() {
    return (
      <div>
        counter is: {this.state.counter}
        <button onClick={this.handleClick}>点我</button>
      </div>
    )
  }
}
ReactDOM.render(<App />, document.getElementById('root'))

每点击一下该按钮,结果会是 + 1,并不是我们意想中的 + 3

React 会把传入多个 setState的多个 Object “batch” 起来合并成一个。合并成一个就相当于把传入 setState 的多个 Object 进行 shallow merge , 像这样:

const update = {
  counter: counter + 1,
  counter: counter + 1,
  counter: counter + 1
  // 因为三行代码相同,所以会当一句话执行
}

我们可以这样修改

handleClick = () => {
  this.setState((prev) => ({ counter: prev.counter + 1 })) // 注意不要直接修改原数据,如 ++counter / counter++
  this.setState((prev) => ({ counter: prev.counter + 1 }))
  this.setState((prev) => ({ counter: prev.counter + 1 }))
}

传入多个 setState 的多个 Object 会被 shallow Merge,而传入多个 setState 的多个 function 会被**“queue”**起来,queue 里的 function 接收到的 state(上面是 prev )都是前一个 function 操作过的 state。