React setState 同步or异步?

673 阅读2分钟
  1. setState 在生命周期和合成事件中是异步;在原生事件和异步方法里是同步;(异步方法里主要是涉及到js事件循环的宏任务和微任务。)

  2. setState 异步实际上是生命周期和合成事件在更新之前被调用,导致他们拿到的数据不是更新后的值。

  3. setState 在生命周期和合成事件中会进行批量更新优化,即对同一个key进行多次setState,只取最后一次执行。如果是更新多个key,在更新时合并批量更新。

合成事件、生命周期的setState

这两段代码表明在合成事件和生命周期里,setState是异步的,并且多次setState只会更新最后一个设置的值。

// 代码1
import React, { Component } from "react";

export default class App extends Component {
  state = {
    count: 0,
  };

  changeCount = () => {
    console.info(this.state.count); // 0
    this.setState({
      count: 1,
    });
    this.setState({
      count: 3,
    });
    console.info(this.state.count); // 0
  };

  render() {
    return (
      <div>
        <button onClick={this.changeCount}> 点击</button>
        渲染结果等于3 {this.state.count}
      </div>
    );
  }
}
// 代码2
import React, { Component } from "react";

export default class App extends Component {
  state = {
    count: 0,
  };

  componentDidMount() {
    console.info(this.state.count); // 0
    this.setState({
      count: 1,
    });
    this.setState({
      count: 3,
    });
    console.info(this.state.count); // 0
  }

  render() {
    return <div>渲染结果等于3 {this.state.count}</div>;
  }
}

原生事件里的setState

import React, { Component } from "react";

export default class App extends Component {
  state = {
    count: 0,
  };

  changeCount = () => {
    console.info(this.state.count); // 0
    this.setState({
      count: this.state.count + 1,
    });
    console.info(this.state.count); // 1
  };

  componentDidMount() {
    document.getElementById("btn").addEventListener("click", this.changeCount);
  }

  render() {
    return (
      <div>
        <button id="btn">点击{this.state.count}次</button>
      </div>
    );
  }
}

异步函数中的setState

import React, { Component } from "react";

export default class App extends Component {
  state = {
    count: 0,
  };

  componentDidMount() {
    setTimeout(() => {
      console.info(this.state.count); // 0
      this.setState({ count: this.state.count + 1 });
      console.info(this.state.count); // 1
    }, 0);
  }

  render() {
    return <div> {this.state.count}</div>;
  }
}

练习题

最后来看下,这段代码的输出结果:

import React, { Component } from "react";

export default class App extends Component {
  state = {
    count: 0,
  };
  componentDidMount() {
    this.setState({ count: this.state.count + 1 });
    console.log("origin1", this.state.count); // 0  step1
    this.setState({ count: this.state.count + 1 });
    console.log("origin2", this.state.count); // 0 step2
    setTimeout(() => {
      console.log("timeout start", this.state.count); // 3 step9
      this.setState({ count: this.state.count + 1 });
      console.log("timeout1", this.state.count); // 4 step10
      this.setState({ count: this.state.count + 1 });
      console.log("timeout2", this.state.count); // 5 step11
    }, 1000);

    new Promise((reslove) => {
      console.log("Promise start", this.state.count); //0 step3
      this.setState({ count: this.state.count + 1 });
      console.log("Promise1", this.state.count); // 0 setp4
      reslove({});
      this.setState({ count: this.state.count + 1 });
      console.log("Promise2", this.state.count); // 0 setp5
    }).then(() => {
      console.log("then", this.state.count); // 1 setp6
      this.setState({ count: this.state.count + 1 });
      console.log("then1", this.state.count); // 2 setp7
      this.setState({ count: this.state.count + 1 });
      console.log("then2", this.state.count); // 3 setp8
    });
  }
  render() {
    return <div>setState {this.state.count}</div>;
  }
}