你不知道的 React 之 setState() 异步更新数据及其解决办法
1. setState() 的说明
相信大家 React 没少用,但是你知道 React 的 setState() 方法是异步更新数据的吗?下面我们将通过例子来解析 setState() 是如何异步更新数据的,以及使用过程中应该注意些什么。
1.1 更新数据
setState() 是异步更新数据的
众所周知,React 中是通过 setState() 来更新数据的,但是其实 setState() 更新数据是异步的,代码如下:
// 导入ract
import React from 'react'
import ReactDOM from 'react-dom'
// App组件
class App extends React.Component {
// 默认状态的值
state = {
count: 1
}
handleClick = () => {
// 异步更新state
this.setState({
count: this.state.count + 1
})
console.log(this.state.count)// 1
}
render() {
return (
<div>
<h1>计数器:{this.state.count}</h1>
<button onClick={this.handleClick}>+1</button>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'))
在上述代码中,我们有一个默认值为1,并且提供了一个点击事件的处理程序,当点击一次按钮时,我们就在该默认值上+1,从而使 count 的值变为2,但是由于我们是直接在 this.setState() 方法后面打印 this.state.count,那么我们实际上拿到的值还是1,说明虽然 this.setState() 方法调用结束了,但是状态并没有立即更新,也证明 this.setState() 是异步更新数据的。 演示效果如下:
注意:使用该语法时,后面的 setState() 不要依赖于前面的 setState()
实际上 this.setState() 是可以多次调用的,但是由于 this.setState() 是异步更新数据的,所以在第一个 this.setState() 调用完以后再调用第二个 this.setState() 结果不会发生改变。代码如下:
// 导入ract
import React from 'react'
import ReactDOM from 'react-dom'
// App组件
class App extends React.Component {
state = {
count: 1
}
handleClick = () => {
// 前面的setState()
this.setState({
count: this.state.count + 1// 1 + 1
})
console.log('第一次调用完的count:', this.state.count)// 1
// 后面的setState()
this.setState({
count: this.state.count + 1
})
console.log('第二次调用完的count:', this.state.count)// 1
}
render() {
return (
<div>
<h1>计数器:{this.state.count}</h1>
<button onClick={this.handleClick}>+1</button>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'))
效果如下:
可以调用多次 setState(),但是只会调用一次重新渲染
虽然 this.setState() 调用了2次,但是 render() 只执行一次(初始化时不算,只有点击按钮以后才算),这是为了性能更好,不会因为每次状态的改变都要重新渲染页面。代码如下:
// 导入ract
import React from 'react'
import ReactDOM from 'react-dom'
// App组件
class App extends React.Component {
state = {
count: 1
}
handleClick = () => {
// 前面的setState()
this.setState({
count: this.state.count + 1// 1 + 1
})
console.log('第一次调用完的count:', this.state.count)// 1
// 后面的setState()
this.setState({
count: this.state.count + 1
})
console.log('第二次调用完的count:', this.state.count)// 1
}
render() {
console.log('render调用')
return (
<div>
<h1>计数器:{this.state.count}</h1>
<button onClick={this.handleClick}>+1</button>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'))
效果如下:
1.2 推荐语法
如果后一次的 this.setState() 想基于第一次 this.setState() 的结果的基础上做操作,该如何实现?方法如下:
- 推荐:使用 setState((state,props) => {}) 语法
- 参数 state:表示最新的 state
- 参数 props:表示最新的 props
// 导入ract
import React from 'react'
import ReactDOM from 'react-dom'
// App组件
class App extends React.Component {
state = {
count: 1
}
handleClick = () => {
// 前面的setState()
// 更新state
// 注意:这种方法也是异步更新数据
this.setState((state, props) => {
return {
count: state.count + 1// 1 + 1
}
})
// 后面的setState()
this.setState((state, props) => {
console.log('第二次调用:', state)
return {
count: state.count + 1
}
})
console.log('count:', this.state.count)// 1
}
render() {
console.log('render')
return (
<div>
<h1>计数器:{this.state.count}</h1>
<button onClick={this.handleClick}>+1</button>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'))
效果如下:
1.3 setState() 的第二个参数
- 场景:在状态更新(页面完成重新渲染)后立即执行某个操作
- 语法:setSate(updater,[callback])
this.setState(
(state, props) => {},// 参数1
() => {console.log('这个回调函数会在状态更新后立即执行')}// 参数2
)
实际上 setState() 方法还有第二个参数,它是一个回调函数,这个回调函数会在状态更新后立即执行,所以如果你希望在状态更新后做些什么的话,可以在这个回调函数中去执行,其使用方式具体如下:
// 导入ract
import React from 'react'
import ReactDOM from 'react-dom'
// App组件
class App extends React.Component {
state = {
count: 1
}
handleClick = () => {
this.setState(
// 参数1
(state, props) => {
return { count: state.count + 1 }
},
// 参数2:在状态更新后且重新渲染后,立即执行
() => {
console.log('状态更新完成:', this.state.count)
console.log(document.getElementById('title').innerText)
document.title = '更新后的count为:' + this.state.count
}
)
console.log("异步执行完以后的count:"+this.state.count)// 1
}
render() {
return (
<div>
<h1 id='title'>计数器:{this.state.count}</h1>
<button onClick={this.handleClick}>+1</button>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'))
效果如下:
转载自:https://juejin.cn/post/7133127251704922126