文章目錄
- 一文講清楚React中setState的使用方法和機制
- 1. setState是什么
- 2. setState方法詳解
- 2.1 setState參數詳解
- 2.2 setState同步異步問題
- 2.2.1 setState異步更新
- 2.2.2 setState同步更新
一文講清楚React中setState的使用方法和機制
1. setState是什么
- React中,this.setState(updater,callback)是一個方法,用來改變state的值,進而實現頁面的重新渲染
- 不了解的先看這篇文章一文講清楚React中state和props的區別與聯系
- 直接上例子
import React from 'react'
class App extends React.Component{constructor(props){super(props)this.state={name:'tom'}}handleClick=()=>{this.setState({name:'new tom'})}render(){return(<div><div>{this.state.name}</div><button onClick={this.handleClick}>change name in by setState</button></div>)}
}
export default App
- 運行顯示tom,點擊按鈕通過setState方法把name改為new tom,發現頁面顯示成了new tom
2. setState方法詳解
2.1 setState參數詳解
- setState由兩個參數,updater和callback,updater可以是對象也可以是函數
- 我們下一個計數器的demo
import React from 'react'
class App extends React.Component{constructor(props){super(props)this.state={count:0}}handleClick=()=>{this.setState(preState=>({count:preState.count+1}))}render(){return(<div><div>{this.state.count}</div><button onClick={this.handleClick}>+1</button></div>)}
}
export default App
- 點擊+1,也能實現狀態的改變并現實出來
- 那么對象參數和函數參數有什么區別呢,先給出答案,如果是參數是對象,會進行事件合并,如果參數是函數,不會發生事件合并,上代碼
- 我們設置兩個計數器,一個從參數對象更新,一個用參數函數更新,每點擊一次按鈕,計數器做三次+1操作
import React from 'react'
class App extends React.Component{constructor(props){super(props)this.state={count1:0,count2:0,}}handleClick=()=>{//第一個計數器用對象參數執行三次+1this.setState({count1:this.count1+1})this.setState({count1:this.count1+1})this.setState({count1:this.count1+1})//第2個計數器用函數參數執行三次+1this.setState(preState=>({count2:preState.count2+1}))this.setState(preState=>({count2:preState.count2+1}))this.setState(preState=>({count2:preState.count2+1}))}render(){return(<div><div>count1:{this.state.count1}</div><div>count2:{this.state.count2}</div><button onClick={this.handleClick}>+1</button></div>)}
}
export default App
-
運行
-
初始值都是0沒問題
-
然后點按鈕
-
會發現count1只+1,而count2+3,這是因為setState在參數為對象的模式下,對批量操作會進行覆蓋,只取最后一次結果執行
-
setState還有第二個參數,callback,我們在下面講
2.2 setState同步異步問題
2.2.1 setState異步更新
- 先直接上代碼
import React from 'react'
class App extends React.Component{constructor(props){super(props)this.state={count:0}}handleClick=()=>{this.setState(preState=>({count:preState.count+1}))console.log(this.state.count)}render(){return(<div><div>{this.state.count}</div><button onClick={this.handleClick}>+1</button></div>)}
}
export default App
-
運行,處置為.,然后點擊+1按鈕,發現頁面顯示1,但是console.log打印0
-
這是為什么呢,這是因為React在合成事件中,state是異步更新的
-
為什么會是一步的呢,問題就出現在合成事件上,不懂合成事件的看這篇文章一文講清楚React合成事件機制和this的綁定問題
-
因為合成事件的原因,console.log的執行時機別state更新的時機要早,所以造成了異步現象
-
同理,在生命周期函數中改變狀態也會造成異步更新
-
如果想要拿到更新后的state,就需要setState第二個參數callback,他會在UI更新后被調用
-
我們這么改一下代碼
import React from 'react'
class App extends React.Component{constructor(props){super(props)this.state={count:0}}handleClick=()=>{this.setState(preState=>({count:preState.count+1}),()=>{console.log(this.state.count)})}render(){return(<div><div>{this.state.count}</div><button onClick={this.handleClick}>+1</button></div>)}
}
export default App
- 這時候我們拿到了同步的值
2.2.2 setState同步更新
- 上面說因合成事件會導致異步更新,那如果我們使用原生事件呢,上代碼
import React from 'react'
class App extends React.Component{constructor(props){super(props)this.state={count:0}}componentDidMount(){document.getElementById('update').addEventListener('click',this.handleClick)}handleClick=()=>{this.setState(preState=>({count:preState.count+1}))console.log(this.state.count)}render(){return(<div><div>{this.state.count}</div><button id="update">+1</button></div>)}
}
export default App
- 運行會發現,還是打印0,哈哈哈,那是因為React在V16版本前,在原生事件和setTimeout中setState是同步事件,這里本來想演示這個來著,但是奈何坐著的React的版本比較高,這里建議大家切換低版本自己運行一下,代碼肯定是沒有任何問題的
- React為了讓setState在所有場景下保持一致性,從V16+開始,無論是合成事件、原生事件還是setTimeout都是保持異步,要獲取更新后的狀態請統一使用setState第二個回調函數