setState同步异步问题

347 阅读2分钟

image.png 在React18版本中,setState无论是在原生事件、setTimeout、promise等方法中都是异步处理,并进行批量更新,但在18版本之前setState是同步处理。

1.setState为什么被设置为异步处理

  1. 设置为异步可以显著提升性能,如果每次调用setState都进行一次更新,那么意味着render函数会被频繁的调用界面重新渲染。React18将获取到多个更新,之后进行批量更新。
  2. 如果同步更新了state,但还没有执行render函数,那么state和props不能保持同步,这对开发是很大问题

2.setState中的批量更新

export class App extends Component {
  constructor(){
    super()
    this.state = {
      val:0
    }
  }
  
  batchUpdates = () => {
    const {val} = this.state
    this.setState({ val: val + 1 })
    console.log(val,'once');
    this.setState({ val: val + 1 })
    console.log(val,'twice');
    this.setState({ val: val + 1 })
    console.log(val,'thrice');
  }
  render() {
    const {val} = this.state
    console.log('render被执行');
    return (
      <div>
        <h2>{val}</h2>
        <button onClick={this.batchUpdates}>自增</button>
      </div>
    )
  }
}

如上代码,batchUpdates函数设置三次setState对val进行加一操作,但实际结果却是每次点击按钮val值只会自加一次,并且render函数执行了一次,同时这也验证了在React18中setState中是异步的,否则会打印出1,2,3

image.png

3.state和props保持同步更新

现在使用setState对val值进行修改,并将val值传给子组件,如果setState是同步的,render函数并不会执行,传递给子组件的val值依然还是0

4.setState在原生事件中

class App extends Component {
  state = { val: 0 }
  changeValue = () => {
    this.setState({ val: this.state.val + 1 })
    console.log(this.state.val) // 输出的是更新后的值 --> 1
  }
 componentDidMount() {
    document.body.addEventListener('click', this.changeValue, false)
 }
  render() {
    return (
      <div>
        {`Counter is: ${this.state.val}`}
      </div>
    )
  }
}

image.png

5.setState在生命周期中

componentDidMount() {
    const {val} = this.state
    this.setState({ val: val + 1 })
    console.log(val,'first')

    this.setState({ val: val + 1 })
    console.log(val,'second')

    setTimeout(_ => {
      this.setState({ val: val + 1 })
      console.log(val,'third');

      this.setState({ val: val + 1 })
      console.log(val,'forth')
    }, 0)
  }

image.png

6.在React18中执行同步

import React, { Component } from "react";
import { flushSync } from "react-dom";

export class App extends Component {
  constructor(){
    super()
    this.state = {
      val:0
    }
  }
  componentDidMount() {
   flushSync(()=>{
    this.setState({val:1})
   })
  }
    render() {
    console.log('render');
    const {val} = this.state
    return (
      <div>
        <h2>{val}</h2>
      </div>
    )
  }
}


export default App;

image.png

7.总结

在React18之前

  1. 在组件生命周期或React合成事件中,setState是异步
  2. 在setTimeout、原生dom事件中、promise中,setState是同步

在React18中统一是异步的