关于React中setState同步或异步问题的理解

2022-04-15 0 1,187
目录
  • 1. setState同步?异步?
  • 2. 表现为异步
    • 1. React 合成事件
    • 2. 生命周期函数
  • 3. 表现为同步
    • 1. 原生事件
    • 2. setTimeout
  • 4. setState的第二个参数

    1. setState同步?异步?

    在 React 的类式组件中,我们可以使用setState方法更新state状态。但有些时候使用setState之后,得不到最新的数据。

    其实 React 中setState本身执行的过程和代码是同步的,只是因为 React 框架本身的性能优化机制而导致的。React 中合成事件和生命周期函数的调用顺序在更新之前,导致在合成事件和生命周期函数中无法立刻得到更新后的值,形成了异步的形式。

    假如在一个合成事件中,循环调用了setState方法n次,如果 React 没有优化,当前组件就要被渲染n次,这对性能来说是很大的浪费。所以,React 为了性能原因,对调用多次setState方法合并为一个来执行。当执行setState的时候,state中的数据并不会马上更新。

    前面已经说到,在 React 的合成事件和生命周期函数中直接调用setState,会表现出异步的形式。

    除此之外,如果越过 React 的性能优化机制,在原生事件、setTimeout中使用setState,就会表现出同步的形式。

    2. 表现为异步

    1. React 合成事件

    在 React 中直接使用的事件,如onChange、onClick等,都是由 React 封装后的事件,是合成事件,由 React 管理。那么由于性能优化的机制,在合成事件中直接调用setState,将表现出异步的形式。

    如下代码,在合成事件onClick中,直接将state中的count加1,并在此之后打印count的值,结果第一次点击按钮时,会打印出0,而不是最新的1。

    state = { count: 0 };
    add = () => {
        this.setState({ count: this.state.count + 1 });
        console.log(this.state.count); // 0
    };
    render() {
        return (
            <>
                <div>当前计数:{this.state.count}</div>
                <button onClick={this.add}>add</button>
            </>
        );
    }

    2. 生命周期函数

    生命周期函数也是由 React 所管理,在生命周期函数中直接调用setState,也会表现出异步的形式。

    如下代码,在生命周期componentDidMount函数中,将state中的count加1,并在此之后打印count的值,结果打印出0,而不是最新的1。

    state = { count: 0 };
    componentDidMount() {
        this.setState({ count: this.state.count + 1 });
        console.log(this.state.count); // 0
    }
    render() {
        return (
            <>
                <div>当前计数:{this.state.count}</div>
                <button>add</button>
            </>
        );
    }

    3. 表现为同步

    1. 原生事件

    setState本身执行的过程是同步的,使用原生事件,绕过 React 的管理,将表现出同步的形式。

    如下代码,通过id获取到 DOM 元素,用原生方法绑定点击事件。在点击事件中,将state中的count加1,并在此之后打印count的值,结果会打印最新的count值1。

    state = { count: 0 };
    componentDidMount() {
        const btn = document.getElementById('btn');
        btn.onclick = () => {
            this.setState({ count: this.state.count + 1 });
            console.log(this.state.count); // 1
        };
    }
    render() {
        return (
            <>
                <div>当前计数:{this.state.count}</div>
                <button id="btn">add</button>
            </>
        );
    }

    2. setTimeout

    如下代码,在生命周期componentDidMount函数中写了一个定时器setTimeout,在setTimeout内部将state中的count加1,并在此之后打印count的值,结果会打印最新的count值1。

    setState虽然也是写在生命周期componentDidMount函数中的,但并不是直接写在componentDidMount里,而是套了一层setTimeout。这样,setState就表现出同步的形式。

    state = { count: 0 };
    componentDidMount() {
        setTimeout(() => {
            this.setState({ count: this.state.count + 1 });
            console.log(this.state.count); // 1
        }, 0);
    }
    render() {
        return (
            <>
                <div>当前计数:{this.state.count}</div>
                <button>add</button>
            </>
        );
    }

    4. setState的第二个参数

    无论setState的对象式写法,还是函数式写法,都有第二个参数,为可选的回调函数,这个回调函数在状态更新完毕、界面也更新后(render调用后)才被调用。

    如下代码所示,setState虽然直接在componentDidMount中调用,但在setState的回调函数中打印count的值,得到了最新的值1,因为回调函数在状态更新完毕后才被调用,当然能得到最新的count了。

    state = { count: 0 };
    componentDidMount() {
        this.setState({ count: this.state.count + 1 }, () => {
            console.log(this.state.count); // 1
        });
    }
    render() {
        return (
            <>
                <div>当前计数:{this.state.count}</div>
                <button>add</button>
            </>
        );
    }
    

    到此这篇关于关于React中setState同步或异步问题的理解的文章就介绍到这了,更多相关React中setState同步或异步内容请搜索NICE源码以前的文章或继续浏览下面的相关文章希望大家以后多多支持NICE源码!

    免责声明:
    1、本网站所有发布的源码、软件和资料均为收集各大资源网站整理而来;仅限用于学习和研究目的,您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。 不得使用于非法商业用途,不得违反国家法律。否则后果自负!

    2、本站信息来自网络,版权争议与本站无关。一切关于该资源商业行为与www.niceym.com无关。
    如果您喜欢该程序,请支持正版源码、软件,购买注册,得到更好的正版服务。
    如有侵犯你版权的,请邮件与我们联系处理(邮箱:skknet@qq.com),本站将立即改正。

    NICE源码网 JavaScript 关于React中setState同步或异步问题的理解 https://www.niceym.com/19510.html