前言
在刚开始学习react的时候,我们会经常使用到setState改变状态,既然用的多,那么就应该思考:==为什么要使用setState?== ==setState是'异步'的吗?== 本篇文章就围绕着这两个问题展开
为什么要使用setState?
先来看一个简单的setState例子:
这是一个比较基础的例子,点击按钮改变state中msg的值
import React, { Component } from 'react'
export class App extends Component {
state = {
msg: 'hello world'
}
render() {
return (
<div>App
<h2>{this.state.msg}</h2>
<button onClick={this.change}>change</button>
</div>
)
}
// setState继承自Component
change = () => {
this.setState({msg: 'haha'})
}
}
export default App
==那么我们为什么要使用setState?==
因为我们修改了状态state的时候,希望React根据最新的state来重新渲染界面,直接修改的方式,react并不会知道状态发生了改变。
react没有实现类似于Vue2中Object.defineProperty或者是Vue3 proxy的方式来监听数据的变化,必须通过setState来告知react状态的改变。
setState是继承自Component,当我们调用setState的时候,会重新执行render方法。
setState是'异步'的吗
对于这个问题,还是用以下不同情形的例子来进行解答:
例一:生命周期函数中使用
1.在componentDidMount函数中使用 this.setState赋值,并在它和componentDidUpdate函数中进行打印
import React, { Component } from 'react'
export class App extends Component {
state = {
count: 0
}
componentDidMount() {
this.setState({count: 1})
console.log(this.state.count) // 0
}
componentDidUpdate() {
console.log(this.state.count) // 1
}
render() {
return (
<div></div>
)
}
// setState继承自Component
change = () => {
this.setState({msg: 'haha'})
}
}
export default App
查看结果,componentDidUpdate函数中打印0,componentDidUpdate函数打印1,这说明setState在生命周期是异步的
来看下一个例子
例二:在合成事件中使用
2.点击按钮触发change事件,事件中使用this.setState修改state中count的值,并打印this.state.count
import React, { Component } from 'react'
// 在合成事件里,setState是异步的
export class App extends Component {
state = {
count: 0
}
render() {
return (
<div>App
<h2>{this.state.count}</h2>
{/* 这个就是合成事件 */}
<button onClick={this.change}>change</button>
</div>
)
}
// setState继承自Component
change = () => {
this.setState({count: 1})
console.log(this.state.count) // 0
}
}
export default App
运行查看结果
这里点击了按钮,上面数据已经更改为1了,但是控制台打印的却是9,这是为何呢?
==理论来讲执行代码的过程是同步的,从上往下。出现异步的结果是因为合成事件和生命周期中,调用顺序是在更新之前,所以导致我们没有办法立即拿到更新后的值所以才形成了所谓的异步现象。这里说的异步并不是指内部由异步代码实现的==
例三:在setTimeout中使用
3.写一个定时器的例子,在里面使用this.setState修改值并打印:
import React, { Component } from 'react'
export class App extends Component {
state = {
count: 0
}
componentDidMount() {
setTimeout(() => {
this.setState({count: 1})
console.log(this.state.count) // 1
}, 0)
}
render() {
return (
<div>App
<h2>{this.state.count}</h2>
</div>
)
}
}
export default App
查看结果发现打印的是1,说明setTimeout里setState是同步的
例四:在原生事件中使用
- 再来一个原生事件的例子,点击按钮触发click,更改数据并且打印:
import React, { Component } from 'react'
// 在原生事件中是同步的
export class App extends Component {
state = {
count: 0
}
componentDidMount() {
document.querySelector('button').addEventListener(
'click',
(e) => {
this.click()
},
false
)
}
click() {
this.setState({count: 1})
console.log(this.state.count) // 1
}
render() {
return (
<div>App
<h2>{this.state.count}</h2>
<button>按钮</button>
</div>
)
}
}
export default App
最后的运行结果为1,说明在原生事件中是同步的
总结:
setState在生命周期和合成事件里面是异步的,在setTimeout和原生事件中是同步的