原创

调研react的setState异步与同步


关于setState

官网说明 https://zh-hans.reactjs.org/docs/faq-state.html#what-does-setstate-do

setstate在原生事件,setTimeout,setInterval,promise等异步操作中,state会同步更新

文档提到setState是异步的https://zh-hans.reactjs.org/docs/faq-state.html#when-is-setstate-asynchronous

那再了解一下setState是否能同步执行,答案是

1、原生事件同步

import React, {Component} from 'react';
class Test4 extends Component {
    state = {
        n1: 0,
        n2: 0
    }
    onTab1(){
		this.setState({
            n1: this.state.n1 + 1
        })
        console.log("第一个n1",this.state.n1)
        this.setState({
            n1: this.state.n1 + 1
        })
        console.log("第二个n1",this.state.n1)
    }
    onTab2(){
        this.setState({
            n2: this.state.n2 + 1
        })
		console.log("第一个n2",this.state.n2)
        this.setState({
            n2: this.state.n2 + 1
        })
		console.log("第二个n2",this.state.n2)
    }
    componentDidMount() {
        document.getElementById("btn").addEventListener('click', this.onTab2.bind(this), false);
    }
    render() {
        return (
            <div style={{padding: "50px"}}>
                <h1>n1:{this.state.n1}</h1>
                <h1>n2:{this.state.n2}</h1>
                <button type='button' onClick={this.onTab1.bind(this)}>合成事件测试同步</button>
                <button type='button' id="btn">原生事件测试同步</button>
            </div>
        );
    }
}

export default Test4;

onClick是react的合成事件,setState表现为异步。
addEventListener为原生事件,表现为同步

2、setTimeout同步

import React, {Component} from 'react';

class Test4 extends Component {
    state = {
        n1: 0,
        n2: 0
    }
    onTab1(){
        this.setState({
            n1: this.state.n1 + 1
        })
        console.log("第一个n1",this.state.n1)
        this.setState({
            n1: this.state.n1 + 1
        })
        console.log("第二个n1",this.state.n1)
    }
    onTab2(){
        setTimeout(()=>{
            this.setState({
                n2: this.state.n2 + 1
            })
            console.log("第二个n2",this.state.n2)
            this.setState({
                n2: this.state.n2 + 1
            })
            console.log("第二个n2",this.state.n2)
        },500)
    }

    render() {
        return (
            <div style={{padding: "50px"}}>
                <h1>n1:{this.state.n1}</h1>
                <h1>n2:{this.state.n2}</h1>
                <button type='button' onClick={this.onTab1.bind(this)}>合成事件测试同步</button>
                <button type='button' onClick={this.onTab2.bind(this)}>setTimeout测试同步</button>
            </div>
        );
    }
}

export default Test4;

3、promose同步

import React, {Component} from 'react';

class Test4 extends Component {
    state = {
        n1: 0,
        n2: 0
    }
    onTab1(){
        this.setState({
            n1: this.state.n1 + 1
        })
        console.log("第一个n1",this.state.n1)
        this.setState({
            n1: this.state.n1 + 1
        })
        console.log("第二个n1",this.state.n1)
    }
    onTab2(){
        new Promise(((resolve, reject) => {
            resolve()
        })).then(()=>{
            this.setState({
                n2: this.state.n2 + 1
            })
            console.log("第二个n2",this.state.n2)
            this.setState({
                n2: this.state.n2 + 1
            })
            console.log("第二个n2",this.state.n2)
        })
    }

    render() {
        return (
            <div style={{padding: "50px"}}>
                <h1>n1:{this.state.n1}</h1>
                <h1>n2:{this.state.n2}</h1>
                <button type='button' onClick={this.onTab1.bind(this)}>合成事件测试同步</button>
                <button type='button' onClick={this.onTab2.bind(this)}>Promise测试同步</button>
            </div>
        );
    }
}

export default Test4;

this.setState原理

setstate方法

ReactComponent.prototype.setState = function (partialState, callback) {
  //  将setState事务放进队列中
  this.updater.enqueueSetState(this, partialState);
  if (callback) {
    this.updater.enqueueCallback(this, callback, 'setState');
  }
};

enqueueSetState: function (publicInstance, partialState) {
     // 获取当前组件的instance
    var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, 'setState');

     // 将要更新的state放入一个数组里
     var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []);
    queue.push(partialState);

     //  将要更新的component instance也放在一个队列里
    enqueueUpdate(internalInstance);
  }
  
  function enqueueUpdate(component) {
 // 如果没有处于批量创建/更新组件的阶段,则处理update state事务
 if (!batchingStrategy.isBatchingUpdates) {
   batchingStrategy.batchedUpdates(enqueueUpdate, component);
   return;
 }
 // 如果正处于批量创建/更新组件的过程,将当前的组件放在dirtyComponents数组中
 dirtyComponents.push(component);
}

var ReactDefaultBatchingStrategy = {
  // 用于标记当前是否出于批量更新
  isBatchingUpdates: false,
  // 当调用这个方法时,正式开始批量更新
  batchedUpdates: function (callback, a, b, c, d, e) {
    var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates;

    ReactDefaultBatchingStrategy.isBatchingUpdates = true;

    // 如果当前事务正在更新过程在中,则调用callback,既enqueueUpdate
    if (alreadyBatchingUpdates) {
      return callback(a, b, c, d, e);
    } else {
    // 否则执行更新事务
      return transaction.perform(callback, null, a, b, c, d, e);
    }
  }
};


batchedUpdates 发起 transaction.perform() 事务
这个事务流程中的 anyMethod 是 runBatchedUpdates ,即更新组件状态并走一遍组件生命周期,在 componentWillMount 时将 A 状态队列里累积的状态都依次处理了。
wrapper close,循环遍历 dirtyComponents 并执行 transaction.perform(runBatchedUpdates, null, transaction);
于是 A 就被捡起来开始走事务流程直到 dirtyComponents 里最后一个组件跑完流程,组件都被统一更新了一遍。

以上源码内容分析来自https://www.jianshu.com/p/89a04c132270

再函数式组件中,都是异步

import React, {useState} from "react";
export default function Fn() {
    const [num1, setNum1] = useState(0);
    const [num2, setNum2] = useState(0);
    const onTab1 = () => {
        setNum1(num1 + 1)
        console.log("第一个num1", num1)
        setNum1(num1 + 1)
        console.log("第二个num1", num1)
    }

    const onTab2 = () => {
        setTimeout(()=>{
            setNum2(num2 + 1)
            console.log("第一个num2", num1)
            setNum2(num2 + 1)
            console.log("第二个num2", num1)
        },500)
    }
    return (
        <div style={{padding: "50px"}}>
            <h1>num1:{num1}</h1>
            <h1>num2:{num2}</h1>
            <button type='button' onClick={onTab1}>默认异步</button>
            <button type='button' onClick={onTab2}>setTimeout也是异步</button>
        </div>
    )
}

函数式组件state同步,执行顺序不同步写法

import React, {useState} from "react";
export default function Fn() {
    const [num1, setNum1] = useState(0);
    const [num2, setNum2] = useState(0);
    const onTab1 = () => {
        setNum1(num1 + 1)
        console.log("第一个num1", num1)
        setNum1(num1 + 1)
        console.log("第二个num1", num1)
    }

    const onTab2 = () => {
             setNum2(num2=>num2 + 1)
            console.log("第一个num2", num2)
             setNum2(num2 =>num2 + 1)
            console.log("第二个num2", num2)
    }
    return (
        <div style={{padding: "50px"}}>
            <h1>num1:{num1}</h1>
            <h1>num2:{num2}</h1>
            <button type='button' onClick={onTab1}>默认异步</button>
            <button type='button' onClick={onTab2}>state同步,执行顺序不同步</button>
        </div>
    )
}

注意:class组件和hooks都可以这样

react
  • 作者:零三(联系作者)
  • 最后更新时间:2021-03-26 14:59
  • 版权声明:自由转载-非商用-非衍生-保持署名
  • 转载声明:来源地址 https://web03.cn
  • 评论