这一次彻底搞懂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 API
React 之从视觉暂留到 FPS、刷新率再到显卡、垂直同步再到16ms的故事
React 之从视觉暂留到 FPS、刷新率再到显卡、垂直同步再到16ms的故事
45 0
|
6月前
|
前端开发 JavaScript
React 中 setState 什么时候是同步的,什么时候是异步的
React 中 setState 什么时候是同步的,什么时候是异步的
62 0
|
2天前
|
前端开发
说说React中setState和replaceState的区别?
在 React 中,setState()和 replaceState()是用于更新组件状态的两个方法。它们之间有一些区别
21 0
|
2天前
|
前端开发 算法
React中的setState执行机制?
React中的setState执行机制?
|
2天前
|
前端开发
React中setState方法详细讲解
React中setState方法详细讲解
|
2天前
|
前端开发 JavaScript 算法
react中的setState的执行机制
react中的setState的执行机制
24 0
|
2天前
|
前端开发
【React学习】—SetState的使用(九)
【React学习】—SetState的使用(九)
|
6月前
|
前端开发 JavaScript 数据管理
react数据管理之setState与Props
react数据管理之setState与Props
46 0
|
前端开发 JavaScript
深入理解 React JS 中的 setState
此文主要探讨了 React JS 中的 setState 背后的机制,供深入学习 React 研究之用。 在课程 React.js入门基础与案例开发 中,有些同学会发现 React JS 中的 setState 的表现好像有点怪异,和理解中的 state 更新机制不太一样,下面我们就来简单探讨下 setState 背后的机制。
1436 0
|
2天前
|
设计模式 前端开发 数据可视化
【第4期】一文了解React UI 组件库
【第4期】一文了解React UI 组件库
135 0

热门文章

最新文章