一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第9天,点击查看活动详情。
引言
相信对React熟悉的读者对setState都不会感到陌生,但是通过面试一些小伙伴也好还是与同事讨论也好,发现大部分人都知道setState是异步的,但是如果深入询问什么场景下是异步的,可不可能是同步,什么场景下又是同步的话又都回答不上来,下面就对此问题进行深入分析,希望能够帮助大家彻底搞懂setState机制。
如何使用setState
首先需要明确的是在 React 的使用中,至关重要的是,不要直接去修改 state。例如:this.state.count = 1是无法触发 React 去更新视图的。因为React的机制规定,一个state的更新,首先需要调用 setState 方法,之后再会进行render函数更新视图。
举例:
this.setState({
count: 1
})
setState 同步 OR 异步
我们首先需要明确,从 API 层面上说,它就是普通的调用执行的函数,自然是同步的,因此,这里所说的同步和异步指的是 API 调用后更新 DOM 是同步还是异步的。
同步和异步主要取决于它被调用的环境。
- 如果 setState 在 React 能够控制的范围被调用,它就是异步的。 例如:合成事件处理函数, 生命周期函数, 此时会进行批量更新, 也就是将状态合并后再进行 DOM 更新。
- 如果 setState 在原生 JavaScript 控制的范围被调用,它就是同步的。 例如:原生事件处理函数中, 定时器回调函数中, Ajax 回调函数中, 此时 setState 被调用后会立即更新 DOM 。
setState 异步
setState 方法其实是 “异步” 的。即立即执行之后,是无法直接获取到最新的 state 中的值,需要经过 React 对 state 的所有改变进行合并处理之后,才会去计算新的虚拟dom,再根据最新的虚拟dom去重新渲染真实dom。
举例分析:
import React, { Component } from 'react'
class App extends Component {
state = {
count: 1,
}
handleClick = () => {
this.setState({
count: this.state.count + 1,
})
console.log(this.state.count) // 1
this.setState({
count: this.state.count + 1,
})
console.log(this.state.count) // 1
}
render() {
return (
<>
<button onClick={this.handleClick}>加1</button>
<div>{this.state.count}</div>
</>
)
}
}
export default App
点击按钮触发事件,打印的都是 1,页面显示 count 的值为 2
在异步中:
- 对同一个值进行多次 setState, setState 的批量更新策略会对其进行覆盖,取最后一次的执行结果
- 如果是同时 setState 多个不同的值,在更新时会对其进行合并后进行批量更新。
setState被设计为异步主要便是为了减少性能开销,从而达到性能优化的目地。
性能优化:
假如 每次 setState 都会触发更新流程 的话,那么每一次 setState的调用都会触发一次 re-render,而re-render 这个过程会涉及到对 DOM 的操作,因此会带来较大的性能开销。
因此,setState 异步的一个重要动机就是避免频繁的 re-render,从而达到性能优化的目地。
setState 同步
setState()实际上可以接受一个函数作为参数,函数的首个参数就是上一次的state,以达到 “同步”的目地。
举例分析:
import React, { Component } from 'react'
class App extends Component {
state = {
count: 1,
}
handleClick = () => {
this.setState(prevState => {
return {count: prevState.count + 1};
});
console.log(this.state.count) // 2
this.setState(prevState => {
return {count: prevState.count + 1};
});
console.log(this.state.count) // 3
}
render() {
return (
<>
<button onClick={this.handleClick}>加1</button>
<div>{this.state.count}</div>
</>
)
}
}
export default App
点击按钮触发事件,打印分别是2和3,页面显示 count 的值为 3
总结
-
setState只在合成事件和钩子函数中是“异步”的,在原生事件和setTimeout中都是同步的。 -
setState的批量更新优化是建立在“异步”之上的,在“异步”中如果对同一个值进行多次setState,setState的批量更新策略会对其进行覆盖,取最后一次的执行,如果是同时setState多个不同的值,在更新时会对其进行合并批量更新,以达到性能优化的目的。 -
正常调用
setState()的话会形成 “异步”化,但是可以通过setState()接受一个函数作为参数,函数的首个参数就是上一次的state,以达到 “同步”化。
结语
本文为笔者个人学习笔记,如有谬误,还请告知,万分感谢!如果本文对你有所帮助,还请点个关注点个赞~,您的支持是笔者不断更新的动力。