这一次彻底搞懂React中的setState在更新状态是同步还是异步的?

简介: 这一次彻底搞懂React中的setState在更新状态是同步还是异步的?

问题引入

React中的setState是用来更新状态的重要工具,但是setState是同步的还是异步的,需要我们进行一定的探讨,接下来让我们好好研究研究。

使用setState的两种形式

  1. 函数形式的setState
test1 = () => {
        // 函数形式的setState,函数形式的setState能够接收到两个参数,一个是state,另一个是props
        this.setState(state => ({count: state.count + 1}))
    }
复制代码
  1. 对象形式的setState
test2 = () => {
        // 对象形式的setState
        const count = this.state.count + 1;
        this.setState({count})
    }
复制代码

使用过setState之后能否立即获取到状态更新后的值

答案是不能。

test1 = () => {
        // 函数形式的setState,函数形式的setState能够接收到两个参数,一个是state,另一个是props
        this.setState(state => ({count: state.count + 1}))
        console.log('函数形式的setState更新后:',this.state.count);


image.png

如何立即获取到状态更新后的值

使用setState的第二个参数,这个参数接收的是一个回调函数,这个回调函数会在界面渲染之后调用。

test3 = () => {
        this.setState(state => ({count: state.count + 1}),() => {
            console.log('函数形式的setState更新后:',this.state.count);
        });
    }


image.png

setState()更新状态是同步还是异步的?

回到我们要探讨的正题,setState()更新状态时同步的还是异步的?

判断setState()更新状态时异步还是同步的,主要是看执行setState的位置

  1. 在React控制的回调函数中(生命周期钩子,react事件监听回调)这种情况是异步的。
  2. 在非react控制的异步回调函数中(定时器回调/原生事件监听回调/promise回调)这种情况是同步的。

异步举例

  • 在React事件回调函数中使用setState(异步的)
// React事件回调函数中
update1 = () => {
    console.log('React事件回调函数更新之前:',this.state.count);
    this.setState(state => ({count: state.count + 1}))
    console.log('React事件回调函数更新之后:',this.state.count);
}


image.png

同步举例

  • setTimeout
// 定时器回调
update2 = () => {
    setTimeout(() => {
        console.log('setTimeout 更新之前:', this.state.count);
        this.setState(state => ({ count: state.count + 1 }))
        console.log('setTimeout 更新之后:', this.state.count);
    })
}

image.png

  • 原生onclick
update3 = () => {
    const h1 = this.refs.count;
    h1.onclick = () => {
        console.log('onclick 更新之前:', this.state.count);
        this.setState(state => ({ count: state.count + 1 }))
        console.log('onclick 更新之后:', this.state.count);
    }
}
复制代码

image.png

setState多次调用的问题

下面要讨论的多次调用的问题是基于异步的前提下来讨论的。

情况1:两个函数式setState的情况(不会合并)

// 测试函数式 setState合并 与更新的问题
update5 = () => {
    console.log('测试通过函数式更新setState的合并问题 更新之前:', this.state.count);
    this.setState(state => ({ count: state.count + 1 }))
    console.log('测试通过函数式更新setState的合并问题 更新之后:', this.state.count);
    console.log('测试通过函数式更新setState的合并问题 更新之前:', this.state.count);
    this.setState(state => ({ count: state.count + 1 }))
    console.log('测试通过函数式更新setState的合并问题 更新之后:', this.state.count);
}
复制代码


image.png

情况2:两个对象式setState的情况(会合并)

// 测试对象式 setState合并 与更新的问题
update6 = () => {
    console.log('测试通过对象式更新setState的合并问题 更新之前:', this.state.count);
    this.setState({count: this.state.count + 1})
    console.log('测试通过对象式更新setState的合并问题 更新之后:', this.state.count);
    console.log('测试通过对象式更新setState的合并问题 更新之前:', this.state.count);
    this.setState({count: this.state.count + 1})
    console.log('测试通过对象式更新setState的合并问题 更新之后:', this.state.count);
}
复制代码

情况3:先函数式后对象式(会合并)

update7 = () => {
    console.log('测试通过函数式更新setState的合并问题 更新之前:', this.state.count);
    this.setState(state => ({ count: state.count + 1 }))
    console.log('测试通过函数式更新setState的合并问题 更新之后:', this.state.count);
    console.log('测试通过对象式更新setState的合并问题 更新之前:', this.state.count);
    this.setState({count: this.state.count + 1})
    console.log('测试通过对象式更新setState的合并问题 更新之后:', this.state.count);
}
复制代码


image.png

核心技巧:函数式传入的state总是能够获取到最新的state,但是对象式则不能,但是最后render只会更新一次。

一道经典的setState的面试题(看懂这个,你可能就懂了!)

请问下面的APP组件打印的是什么?

class App extends Component {
    state = {
        count: 0
    }
    // 在生命周期钩子函数中
    componentDidMount() {
        this.setState({ count: this.state.count + 1 })
        this.setState({ count: this.state.count + 1 })
        console.log(this.state.count);
        this.setState(state => ({ count: state.count + 1 }))
        this.setState(state => ({ count: state.count + 1 }))
        console.log(this.state.count);
        setTimeout(() => {
            this.setState(state => ({ count: state.count + 1 }))
            console.log('setTimeout:', this.state.count);
            this.setState(state => ({ count: state.count + 1 }))
            console.log('setTimeout:', this.state.count);
        }, 0)
        Promise.resolve().then(value => {
            this.setState(state => ({ count: state.count + 1 }))
            console.log('Promise',this.state.count);
            this.setState(state => ({ count: state.count + 1 }))
            console.log('Promise:',this.state.count);
        })
    }
    render() {
        const { count } = this.state;
        console.log('render: ', count);
        return (
            <div>
                <h1>当前求和为{count}</h1>
            </div>
        )
    }
}
复制代码

答案

image.png

答案解析(按输出顺序进行解析)

  1. 第一行: react首先会渲染下组件,此时获取到的count值是state中存的初始值,所以是0.
  2. 第2、3行:执行完render之后,会进入componentDidMount钩子函数,遇到两个对象式的setState会进行合并,但由于此时在钩子函数中,获取state是异步的,所以打印的都是0,但是当遇到函数式的setState,则不会合并,此时count的值已经变为了3.
  3. 第四行:此时componentDidMount中出了Promise和setTimeout外都执行了,上面的代码对JS来说都属于同步代码,此时可以进行更新render了,所以打印了render 3.
  4. 第五行:setTimeout和Promise中,由于Promise是微任务,所以优先执行,在执行的时候,这里的setState是同步更新state的,所以调用一次setState就要调用一次render,所以第五行打印的是render: 4.
  5. 第六行:执行log操作,打印的是Promise: 4。。。

剩下的内容均属于JS事件循环的知识了,如果你有不懂的地方可以参考我的专栏中的事件循环机制的基本认知这篇博文。

codeSandBox

相关文章
|
2月前
|
前端开发 JavaScript
学习react基础(3)_setState、state、jsx、使用ref的几种形式
本文探讨了React中this.setState和this.state的区别,以及React的核心概念,包括核心库的使用、JSX语法、类与函数组件的区别、事件处理和ref的使用。
68 3
学习react基础(3)_setState、state、jsx、使用ref的几种形式
|
3月前
|
资源调度 前端开发 API
React Suspense与Concurrent Mode:异步渲染的未来
React的Suspense与Concurrent Mode是16.8版后引入的功能,旨在改善用户体验与性能。Suspense组件作为异步边界,允许子组件在数据加载完成前显示占位符,结合React.lazy实现懒加载,优化资源调度。Concurrent Mode则通过并发渲染与智能调度提升应用响应性,支持时间分片和优先级调度,确保即使处理复杂任务时UI仍流畅。二者结合使用,能显著提高应用效率与交互体验,尤其适用于数据驱动的应用场景。
72 20
|
3月前
|
存储 前端开发 JavaScript
处理 React 应用程序中的异步数据加载
【8月更文挑战第31天】
62 0
|
4月前
|
存储 前端开发 安全
|
4月前
|
前端开发 JavaScript
react18【系列实用教程】useState —— 声明响应式变量(2024最新版)含useState 的异步更新机制,更新的合并,函数传参获取更新值,不同版本异步更新差异,更新对象和数组
react18【系列实用教程】useState —— 声明响应式变量(2024最新版)含useState 的异步更新机制,更新的合并,函数传参获取更新值,不同版本异步更新差异,更新对象和数组
258 0
|
5月前
|
前端开发
前端React篇之React setState 调用的原理、React setState 调用之后发生了什么?是同步还是异步?
前端React篇之React setState 调用的原理、React setState 调用之后发生了什么?是同步还是异步?
|
6月前
|
前端开发
说说React中setState和replaceState的区别?
在 React 中,setState()和 replaceState()是用于更新组件状态的两个方法。它们之间有一些区别
49 0
|
16天前
|
前端开发 JavaScript 开发者
颠覆传统:React框架如何引领前端开发的革命性变革
【10月更文挑战第32天】本文以问答形式探讨了React框架的特性和应用。React是一款由Facebook推出的JavaScript库,以其虚拟DOM机制和组件化设计,成为构建高性能单页面应用的理想选择。文章介绍了如何开始一个React项目、组件化思想的体现、性能优化方法、表单处理及路由实现等内容,帮助开发者更好地理解和使用React。
46 9
|
1月前
|
前端开发
深入解析React Hooks:构建高效且可维护的前端应用
本文将带你走进React Hooks的世界,探索这一革新特性如何改变我们构建React组件的方式。通过分析Hooks的核心概念、使用方法和最佳实践,文章旨在帮助你充分利用Hooks来提高开发效率,编写更简洁、更可维护的前端代码。我们将通过实际代码示例,深入了解useState、useEffect等常用Hooks的内部工作原理,并探讨如何自定义Hooks以复用逻辑。
|
1月前
|
前端开发 JavaScript API
探索React Hooks:前端开发的革命性工具
【10月更文挑战第5天】探索React Hooks:前端开发的革命性工具