React setState方法是同步还是异步

setState在React事件处理函数中或React方法中是异步,在setTimeout, Promise等异步方法中或原生事件中是同步。

一些例子

React事件:

class Test extends Component {
  state = { count: 0 };
  handleIncrease = () => {
    this.setState({ count: this.state.count + 1 });
    console.log(this.state.count);
    this.setState({ count: this.state.count + 1 });
    console.log(this.state.count);
  };
  render() {
    console.log("render", this.state.count);
    const { count } = this.state;
    return (
      

{count}

); } }

如上代码异步更新state,点击button输出0, 0, render 1

setTimeout异步方法中:

class Test extends Component {
  state = { count: 0 };
  handleIncrease = () => {
    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);
    });
  };
  render() {
    console.log("render", this.state.count);
    const { count } = this.state;
    return (
      

{count}

); } }

如上代码同步更新state,点击button输出render 1, 1, render 2, 2

原生事件处理函数中:

class Test extends Component {
  btnRef = React.createRef();
  state = { count: 0 };
  handleIncrease = () => {
    this.setState({ count: this.state.count + 1 });
    console.log(this.state.count);
    this.setState({ count: this.state.count + 1 });
    console.log(this.state.count);
  };
  componentDidMount() {
    this.btnRef.current.addEventListener("click", this.handleIncrease);
  }
  render() {
    console.log("render", this.state.count);
    const { count } = this.state;
    return (
      

{count}

); } }

如上代码同步更新state,点击button输出render 1, 1, render 2, 2

原理

React中有一个执行上下文变量executionContext,当executionContext === BatchedContext时就异步更新state,当executionContext === NoContext时就同步更新state。

由于在调用React事件回调,React生命周期等React可以检测到的方法的时候,React默认通过batchedUpdates方法做了额外处理:调用方法前设置executionContext = BatchedContext,调用方法后把执行上下文恢复到同步executionContext = NoContext,所以方法中的setState是异步更新。

batchedUpdates方法:

let executionContext = NoContext; //默认值
export function batchedUpdates(fn) {
  let preExecutionContext = executionContext;
  executionContext |= BatchedContext;
  fn();
  executionContext = preExecutionContext;
}

当setState在异步方法中时,同步方法执行完后才会调用调用异步任务,此时最后一条语句executionContext = preExecutionContext;已经执行完成,executionContext === NoContext,这时再执行异步任务中的setState, 就会同步更新。

原生事件中由于没有设置executionContext = BatchedContext,所以是同步更新state,通过batchedUpdates方法可以实现异步更新。

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

class Test extends Component {
  btnRef = React.createRef();
  state = { count: 0 };
  handleIncrease = () => {
    this.setState({ count: this.state.count + 1 });
    console.log(this.state.count);
    this.setState({ count: this.state.count + 1 });
    console.log(this.state.count);
  };
  componentDidMount() {
    // this.btnRef.current.addEventListener("click", this.handleIncrease);
    this.btnRef.current.addEventListener("click", () =>
      unstable_batchedUpdates(this.handleIncrease)
    );
  }
  render() {
    console.log("render", this.state.count);
    const { count } = this.state;
    return (
      

{count}

); } } export default Test;

Concurrent模式下的setState

Concurrent模式下优化了setState更新方式,默认都会采用异步更新。

总结

  • 并发模式setState不管在哪里默认都会批量更新;
  • 同步模式用了batchUpdates就是批量更新,没用就是同步更新;
  • 同步模式下,事件函数或生命周期函数setState是批量的,是因为使用了batchUpdates。

你可能感兴趣的:(React setState方法是同步还是异步)