React.setState
首先引入一個(gè)栗子
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 干了什么
說(shuō)一下批量更新
解讀為什么直接修改this.state無(wú)效
要知道setState本質(zhì)是通過(guò)一個(gè)隊(duì)列機(jī)制實(shí)現(xiàn)state更新的。 執(zhí)行setState時(shí)穗泵,會(huì)將需要更新的state合并后放入狀態(tài)隊(duì)列,而不會(huì)立刻更新state,隊(duì)列機(jī)制可以批量更新state。
如果不通過(guò)setState而直接修改this.state盛杰,那么這個(gè)state不會(huì)放入狀態(tài)隊(duì)列中辆琅,下次調(diào)用setState時(shí)對(duì)狀態(tài)隊(duì)列進(jìn)行合并時(shí)测砂,會(huì)忽略之前直接被修改的state,這樣我們就無(wú)法合并了形导,而且實(shí)際也沒(méi)有把你想要的state更新上去环疼。
什么是批量更新 Batch Update
在一些mv*框架中,朵耕,就是將一段時(shí)間內(nèi)對(duì)model的修改批量更新到view的機(jī)制炫隶。比如那前端比較火的React、vue(nextTick機(jī)制,視圖的更新以及實(shí)現(xiàn))為例阎曹。
vue的nextTick機(jī)制 https://www.cnblogs.com/hity-tt/p/6729118.html
html5新特性變動(dòng)觀察器 http://www.cnblogs.com/jscode/p/3600060.html
消息進(jìn)程 http://www.ruanyifeng.com/blog/2013/10/event_loop.html
vue的批量更新體現(xiàn)
- Mutation Observer(變動(dòng)觀察器)是監(jiān)視DOM變動(dòng)的接口伪阶。當(dāng)DOM對(duì)象樹(shù)發(fā)生任何變動(dòng)時(shí),Mutation Observer會(huì)得到通知处嫌。
- 概念上栅贴,它很接近事件∶趟可以理解為筹误,當(dāng)DOM發(fā)生變動(dòng)會(huì)觸發(fā)Mutation Observer事件。但是癣缅,它與事件有一個(gè)本質(zhì)不同:事件是同步觸發(fā)厨剪,也就是說(shuō)DOM發(fā)生變動(dòng)立刻會(huì)觸發(fā)相應(yīng)的事件;
- Mutation Observer則是異步觸發(fā)友存,DOM發(fā)生變動(dòng)以后祷膳,并不會(huì)馬上觸發(fā),而是要等到當(dāng)前所有DOM操作都結(jié)束后才觸發(fā)屡立。
- 這樣設(shè)計(jì)是為了應(yīng)付DOM變動(dòng)頻繁的情況直晨。舉例來(lái)說(shuō),如果在文檔中連續(xù)插入1000個(gè)段落(p元素),會(huì)連續(xù)觸發(fā)1000個(gè)插入事件勇皇,執(zhí)行每個(gè)事件的回調(diào)函數(shù)罩句,這很可能造成瀏覽器的卡頓;
- 而Mutation Observer完全不同敛摘,只在1000個(gè)段落都插入結(jié)束后才會(huì)觸發(fā)门烂,而且只觸發(fā)一次。
setState之后發(fā)生的事情
- 在官方的描述中兄淫,setState操作并不保證是同步的屯远,也可以認(rèn)為是異步的。
- React在setState之后捕虽,會(huì)經(jīng)對(duì)state進(jìn)行diff慨丐,判斷是否有改變,然后去diff dom決定是否要更新UI泄私。如果這一系列過(guò)程立刻發(fā)生在每一個(gè)setState之后房揭,就可能會(huì)有性能問(wèn)題。
- 在短時(shí)間內(nèi)頻繁setState晌端。React會(huì)將state的改變壓入棧中崩溪,在合適的時(shí)機(jī),批量更新state和視圖斩松,達(dá)到提高性能的效果伶唯。
總結(jié)
- 通過(guò)setState去更新this.state,不要直接操作this.state惧盹,請(qǐng)把它當(dāng)成不可變的乳幸。
- 調(diào)用setState更新this.state不是馬上生效的,它是異步滴钧椰,所以不要天真以為執(zhí)行完setState后this.state就是最新的值了粹断。
- 多個(gè)順序執(zhí)行的setState不是同步地一個(gè)一個(gè)執(zhí)行滴,會(huì)一個(gè)一個(gè)加入隊(duì)列嫡霞,然后最后一起執(zhí)行瓶埋,即批處理
如何知道state已經(jīng)被更新
傳入回調(diào)函數(shù)
setState({
index: 1
}}, function(){
console.log(this.state.index);
})
在鉤子函數(shù)中體現(xiàn)
componentDidUpdate(){
console.log(this.state.index);
}
setState的另外一種方式 (需要使用上一次的state的值)
在setState的第一個(gè)參數(shù)中傳入function,該function會(huì)被壓入調(diào)用棧中诊沪,在state真正改變后养筒,按順序回調(diào)棧里面的function。該function的第一個(gè)參數(shù)為上一次更新后的state端姚。這樣就能確保你下一次的操作拿到的是你預(yù)期的值
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};
});
}
}
注意點(diǎn)
- setState可能會(huì)引發(fā)不必要的渲染(renders)
- setState無(wú)法完全掌控應(yīng)用中所有組件的狀態(tài)