react中setState是同步还是异步 ?如何控制 ?

1. React 中 setState 是同步还是异步 ? 如何控制 ?

setState是异步还是同步与react批量更新有很大关系,react为了优化性能,采用的是批量更新,状态队列更新机制

先看异步情况:

import React, { Component } from 'react';

class com2 extends Component {

    state = {
        num: 0
    }

    add = () => {
        this.setState({
            num: this.state.num + 1
        })
        console.log(this.state.num)

        this.setState({
            num: this.state.num + 2
        })
        console.log(this.state.num)

        this.setState({
            num: this.state.num + 3
        })
        console.log(this.state.num)

    }


    render() {
        return (
            <div>
                <button onClick={this.add}>按钮{this.state.num}</button>
            </div>
        );
    }
}

export default com2;

在这里插入图片描述

在这里插入图片描述

react 内部为了优化 setStae的批量处理,会对 setState 进行一个批量处理的原则,并且对相同的属性进行合并保留最后一次进行更新,所以按钮上面是 3 可以理解的

2.但是打印出来三次 0 这是为啥 ?

React setState 函数实现中,会根据一个变量 isBatchingUpdates 判断是直接更新还是 this.state 还是放到一个 updateQueue 去延迟更新,而 isBatchingUpdates 默认是 false 表示 setStaet 会同步更新 this.state 但是,有一个函数,batchedUpdates ,改函数会把 isBatchingUpdates 修改为 true 而当 React 在调用事件处理函数之前就会先调用这个 batchedUpdatesisBatchingUpdates修改为true,这样由 React 控制的事件处理过程 setState 不会同步更新 this.state,而是异步的。

3.总结一下:

setState 本身是同步的 一但走了 react内部合并的一个逻辑 ,放入了updateQueue队列中就变成异步了而代码中的函数是react控制的,内部会走合并逻辑,所以这里的setState 不但是合并的也是异步的,所以打印出三个0

4.控制 setState 的同步异步:

上面说了 setState是异步的原因是因为走了react内部合并的一个逻辑 因此为了达到同步那只能绕开react内部合并的一个逻辑不让它进入 updateQueue那么它就成了同步的 因为setState()本身就是同步的

5.利用setTimeout绕过react内部的合并逻辑:
import React, { Component } from 'react';
class com2 extends Component {

    state = {
        num: 0
    }

    add = () => {
			//利用setTimeout绕过react的控制,不让setState()走合并逻辑
        setTimeout(() => {
            this.setState({
                num: this.state.num + 1
            })
            console.log(this.state.num)

            this.setState({
                num: this.state.num + 2
            })
            console.log(this.state.num)

            this.setState({
                num: this.state.num + 3
            })
            console.log(this.state.num)
        });

    }


    render() {
        return (
            <div>
                <button onClick={this.add}>按钮{this.state.num}</button>
            </div>
        );
    }
}

export default com2;

结果如下:

在这里插入图片描述

在这里插入图片描述

总结:
异步的情况:
由React控制的事件处理函数,以及生命周期函数调用setState时表现为异步 。大部分开发中用到的都是React封装的事件,比如onChange、onClick、onTouchMove等(合成事件中),这些事件处理函数中的setState都是异步处理的。

同步的情况:
React控制之外的事件中调用setState是同步更新的。比如原生js绑定的事件,setTimeout/setInterval,ajax,promise.then内等 React 无法掌控的 APIs情况下,setState是同步更新state的

值得一提的是

setState()可以接收一个对象外,还可以接收一个函数:
区别:

传递对象
批处理,对相同变量进行的多次处理会合并为一个,并以最后一次的处理结果为准

传递函数
链式调用,React 会把我们更新 state 的函数加入到一个队列里面,然后,按照函数的顺序依次调用。同时,为每个函数传入 state 的前一个状态,这样,就能更合理的来更新我们的 state 了,该函数有两个参数

prevState

props

6.可以理解为:

本来我想拿到上一次setState() 执行完后的结果,需要使用一些特殊的方式,绕开合并逻辑,让setState() 保持本身的同步执行特性,代码如下:

import React, { Component } from 'react';

class com2 extends Component {

    state = {
        count: 0
    }

    add = () => {
        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)
            this.setState({
                count: this.state.count + 1
            })
            console.log(this.state.count)
        })

    }

    render() {
        return (
            <div>
                <button onClick={this.add}>按钮{this.state.count}</button>
            </div>
        );
    }
}

export default com2;

点击后结果:

在这里插入图片描述

在这里插入图片描述

而现在,在异步执行的情况下,我还想拿到上一次setState执行完后的结果,react给我们提供了方式,就是在setState()里面传入一个函数,代码如下:

import React, { Component } from 'react';

class com2 extends Component {

    state = {
        count: 0
    }

    add = () => {
        this.setState((state) => {
            // 重要:在更新的时候读取 `state`,而不是 `this.state`。
            return { count: state.count + 1 }
        });
        console.log(this.state.count)
        this.setState((state) => {
            // 重要:在更新的时候读取 `state`,而不是 `this.state`。
            return { count: state.count + 1 }
        });
        console.log(this.state.count)
        this.setState((state) => {
            // 重要:在更新的时候读取 `state`,而不是 `this.state`。
            return { count: state.count + 1 }
        });
        console.log(this.state.count)

    }

    render() {
        return (
            <div>
                <button onClick={this.add}>按钮{this.state.count}</button>
            </div>
        );
    }
}

export default com2;

结果如下:

在这里插入图片描述

在这里插入图片描述

在setState中传一个函数,能拿到上次setState执行完后的结果,但是不妨碍是异步更新的,可以看到打印的是0,`这是react给我们提供的方便之处

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值