React.setState
首先引入一個栗子
class Example extends React.Component {
constructor() {
super();
this.state = {
val: 0
};
}
componentDidMount() {
this.setState({val: this.state.val + 1});
console.log(this.state.val); // 第 1 次 log
this.setState({val: this.state.val + 1});
console.log(this.state.val); // 第 2 次 log
setTimeout(() => {
this.setState({val: this.state.val + 1});
console.log(this.state.val); // 第 3 次 log
this.setState({val: this.state.val + 1});
console.log(this.state.val); // 第 4 次 log
}, 0);
}
render() {
return null;
}
};
複製程式碼
4次log的值 分別為 0 0 2 3
setState 幹了什麼
說一下批量更新
解讀為什麼直接修改this.state無效
要知道setState本質是通過一個佇列機制實現state更新的。 執行setState時,會將需要更新的state合併後放入狀態佇列,而不會立刻更新state,佇列機制可以批量更新state。 如果不通過setState而直接修改this.state,那麼這個state不會放入狀態佇列中,下次呼叫setState時對狀態佇列進行合併時,會忽略之前直接被修改的state,這樣我們就無法合併了,而且實際也沒有把你想要的state更新上去。
什麼是批量更新 Batch Update
在一些mv*框架中,,就是將一段時間內對model的修改批量更新到view的機制。比如那前端比較火的React、vue(nextTick機制,檢視的更新以及實現)為例。
vue的nextTick機制 www.cnblogs.com/hity-tt/p/6…
html5新特性變動觀察器 www.cnblogs.com/jscode/p/36…
訊息程式 www.ruanyifeng.com/blog/2013/1…
vue的批量更新體現
- Mutation Observer(變動觀察器)是監視DOM變動的介面。當DOM物件樹發生任何變動時,Mutation Observer會得到通知。
- 概念上,它很接近事件。可以理解為,當DOM發生變動會觸發Mutation Observer事件。但是,它與事件有一個本質不同:事件是同步觸發,也就是說DOM發生變動立刻會觸發相應的事件;
- Mutation Observer則是非同步觸發,DOM發生變動以後,並不會馬上觸發,而是要等到當前所有DOM操作都結束後才觸發。
- 這樣設計是為了應付DOM變動頻繁的情況。舉例來說,如果在文件中連續插入1000個段落(p元素),會連續觸發1000個插入事件,執行每個事件的回撥函式,這很可能造成瀏覽器的卡頓;
- 而Mutation Observer完全不同,只在1000個段落都插入結束後才會觸發,而且只觸發一次。
setState之後發生的事情
- 在官方的描述中,setState操作並不保證是同步的,也可以認為是非同步的。
- React在setState之後,會經對state進行diff,判斷是否有改變,然後去diff dom決定是否要更新UI。如果這一系列過程立刻發生在每一個setState之後,就可能會有效能問題。
- 在短時間內頻繁setState。React會將state的改變壓入棧中,在合適的時機,批量更新state和檢視,達到提高效能的效果。
總結
- 通過setState去更新this.state,不要直接操作this.state,請把它當成不可變的。
- 呼叫setState更新this.state不是馬上生效的,它是非同步滴,所以不要天真以為執行完setState後this.state就是最新的值了。
- 多個順序執行的setState不是同步地一個一個執行滴,會一個一個加入佇列,然後最後一起執行,即批處理
如何知道state已經被更新
傳入回撥函式
setState({
index: 1
}}, function(){
console.log(this.state.index);
})
複製程式碼
在鉤子函式中體現
componentDidUpdate(){
console.log(this.state.index);
}
複製程式碼
setState的另外一種方式 (需要使用上一次的state的值)
在setState的第一個引數中傳入function,該function會被壓入呼叫棧中,在state真正改變後,按順序回撥棧裡面的function。該function的第一個引數為上一次更新後的state。這樣就能確保你下一次的操作拿到的是你預期的值
lass Com extends React.Component{
constructor(props){
super(props);
this.state = {
index: 0
}
this.add = this.add.bind(this);
}
add(){
this.setState(prevState => {
return {index: prevState.index + 1};
});
this.setState(prevState => {
return {index: prevState.index + 1};
});
}
}
複製程式碼
注意點
- setState可能會引發不必要的渲染(renders)
- setState無法完全掌控應用中所有元件的狀態