# [React中setState同步更新策略](https://www.cnblogs.com/jiuyi/p/9263114.html)
# setState 同步更新
我們在上文中提及,為了提高性能React將setState設置為批次更新,即是異步操作函數,并不能以順序控制流的方式設置某些事件,我們也不能依賴于this.state來計算未來狀態。典型的譬如我們希望在從服務端抓取數據并且渲染到界面之后,再隱藏加載進度條或者外部加載提示:
[](javascript:void(0); "復制代碼")
~~~
componentDidMount() {
fetch('https://example.com')
.then((res) => res.json())
.then(
(something) => {
this.setState({ something });
StatusBar.setNetworkActivityIndicatorVisible(false);
}
);
}
~~~
[](javascript:void(0); "復制代碼")
因為setState函數并不會阻塞等待狀態更新完畢,因此setNetworkActivityIndicatorVisible有可能先于數據渲染完畢就執行。我們可以選擇在componentWillUpdate與componentDidUpdate這兩個生命周期的回調函數中執行setNetworkActivityIndicatorVisible,但是會讓代碼變得破碎,可讀性也不好。實際上在項目開發中我們更頻繁遇見此類問題的場景是以某個變量控制元素可見性:
~~~
this.setState({showForm : !this.state.showForm});
~~~
我們預期的效果是每次事件觸發后改變表單的可見性,但是在大型應用程序中如果事件的觸發速度快于setState的更新速度,那么我們的值計算完全就是錯的。本節就是討論兩種方式來保證setState的同步更新。
## 完成回調
setState函數的第二個參數允許傳入回調函數,在狀態更新完畢后進行調用,譬如:
[](javascript:void(0); "復制代碼")
~~~
this.setState({
load: !this.state.load,
count: this.state.count + 1
}, () => {
console.log(this.state.count);
console.log('加載完成')
});
~~~
[](javascript:void(0); "復制代碼")
這里的回調函數用法相信大家很熟悉,就是JavaScript異步編程相關知識,我們可以引入Promise來封裝setState:
~~~
setStateAsync(state) {
return new Promise((resolve) => {
this.setState(state, resolve)
});
}
~~~
setStateAsync 返回的是Promise對象,在調用時我們可以使用Async/Await語法來優化代碼風格:
[](javascript:void(0); "復制代碼")
~~~
async componentDidMount() {
StatusBar.setNetworkActivityIndicatorVisible(true)
const res = await fetch('https://api.ipify.org?format=json')
const {ip} = await res.json()
await this.setStateAsync({ipAddress: ip})
StatusBar.setNetworkActivityIndicatorVisible(false)
}
~~~
[](javascript:void(0); "復制代碼")
這里我們就可以保證在setState渲染完畢之后調用外部狀態欄將網絡請求狀態修改為已結束,整個組件的完整定義為:
[](javascript:void(0); "復制代碼")
~~~
class AwesomeProject extends Component {
state = {}
setStateAsync(state) {
...
}
async componentDidMount() {
...
}
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>
My IP is {this.state.ipAddress || 'Unknown'}
</Text>
</View>
);
}
}
~~~
[](javascript:void(0); "復制代碼")
## 傳入狀態計算函數
除了使用回調函數的方式監聽狀態更新結果之外,React還允許我們傳入某個狀態計算函數而不是對象來作為第一個參數。狀態計算函數能夠為我們提供可信賴的組件的State與Props值,即會自動地將我們的狀態更新操作添加到隊列中并等待前面的更新完畢后傳入最新的狀態值:
~~~
this.setState(function(prevState, props){
return {showForm: !prevState.showForm}
});
~~~
這里我們以簡單的計數器為例,我們希望用戶點擊按鈕之后將計數值連加兩次,基本的組件為:
[](javascript:void(0); "復制代碼")
~~~
class Counter extends React.Component{
constructor(props){
super(props);
this.state = {count : 0}
this.incrementCount = this.incrementCount.bind(this)
}
incrementCount(){
...
}
render(){
return <div>
<button onClick={this.incrementCount}>Increment</button>
<div>{this.state.count}</div>
</div>
}
}
~~~
[](javascript:void(0); "復制代碼")
直觀的寫法我們可以連續調用兩次setState函數,這邊的用法可能看起來有點怪異,不過更多的是為了說明異步更新帶來的數據不可預測問題。
~~~
incrementCount(){
this.setState({count : this.state.count + 1})
this.setState({count : this.state.count + 1})
}
~~~
上述代碼的效果是每次點擊之后計數值只會加1,實際上第二個setState并沒有等待第一個setState執行完畢就開始執行了,因此其依賴的當前計數值完全是錯的。我們當然可以使用上文提及的setStateAsync來進行同步控制,不過這里我們使用狀態計算函數來保證同步性:
[](javascript:void(0); "復制代碼")
~~~
incrementCount(){
this.setState((prevState, props) => ({
count: prevState.count + 1
}));
this.setState((prevState, props) => ({
count: prevState.count + 1
}));
}
~~~
[](javascript:void(0); "復制代碼")
這里的第二個setState傳入的prevState值就是第一個setState執行完畢之后的計數值,也順利保證了連續自增兩次。
- 第一章 起步
- 第1節 創建react項目
- 第2節 hello world
- 第3節 數據綁定+事件處理
- 3.1 for循環事件處理中的傳參寫法、條件渲染
- 第4章 點擊切換文字
- 第5章 使用html寫react
- 第二章 運用
- 第1節 循環
- 第2節 實現一個簡單的TodoList
- 第2.1節 刪除
- 第3節 父子組件傳參
- 1. 父組件向子組件傳參
- 2. 子組件向父組件傳參
- 第4節 react-router實現一個簡單路由
- 第5節 生命周期
- 第6節 取數據
- 第 7節 獲取dom節點
- 第8節 雙向數據綁定
- 第三章 redux
- 第1節 介紹
- 第2節 安裝redux
- 第3節 使用
- 3.1 action
- 3.2 使用redux實現 todolist
- 第4節封裝redux中的action
- 第5節 redux-thunk中間件
- 5.1介紹
- 5.2使用
- 第四章 ant-design前端ui
- 第一節 安裝
- 第2節 使用
- 2.1 ant-design實現todoList增刪功能
- 第3節 使用整理
- 第五章 vue和react的比較
- 第六章 dva.js輕量級應用框架
- 第1節 介紹
- 第2節 安裝dva
- 第3節 頁面跳轉
- 1. 事件路由跳轉
- 2. 通過路由跳轉
- 第4節 組件之間通信
- 1. 父組件向子組件傳參
- 2. 子組件向父組件傳參
- 第5節 事件處理
- 第6節 發送請求
- 1. 通過路由判斷頁面渲染數據
- 2. 通過事件發送請求
- 第7節 運用
- 1. TodoList
- 1.添加數據
- 1.2輸入框敲回車觸發事件
- 2.刪除數據
- 3. 總結
- 第8節 配合antd使用
- 1. 引入antd
- 2.dva 使用antd注意事項
- 3. 知識點整理
- 第七章 dva后臺項目實戰
- 第1節 登錄加密
- 1.具體實現
- 第2節 知識點
- 第3節 樹結構
- 第八章 react新特性 hooks
- 第1節 hooks介紹
- 第2節 useState的使用
- 第3節 useEffect的使用
- 第4節 dva+antd+hooks企業后臺項目開發流程
- 第 5節 hooks 使用
- 運用
- 第6節 hook整理
- 第7節 react memo
- 第九章 react中使用Echarts
- 知識點
- react中使用mobx
- 知識點
- react中使用rem
- 遞歸實現目錄數
- react使用圖表
- react 同步更新策略
- antd tree默認展開無效
- ts中lint修復篇
- React-query方案
- 高階組件