react 常見(jiàn)setState的原理解析

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 干了什么

image

說(shuō)一下批量更新

image

解讀為什么直接修改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)

  1. Mutation Observer(變動(dòng)觀察器)是監(jiān)視DOM變動(dòng)的接口伪阶。當(dāng)DOM對(duì)象樹(shù)發(fā)生任何變動(dòng)時(shí),Mutation Observer會(huì)得到通知处嫌。
  2. 概念上栅贴,它很接近事件∶趟可以理解為筹误,當(dāng)DOM發(fā)生變動(dòng)會(huì)觸發(fā)Mutation Observer事件。但是癣缅,它與事件有一個(gè)本質(zhì)不同:事件是同步觸發(fā)厨剪,也就是說(shuō)DOM發(fā)生變動(dòng)立刻會(huì)觸發(fā)相應(yīng)的事件;
  3. Mutation Observer則是異步觸發(fā)友存,DOM發(fā)生變動(dòng)以后祷膳,并不會(huì)馬上觸發(fā),而是要等到當(dāng)前所有DOM操作都結(jié)束后才觸發(fā)屡立。
  4. 這樣設(shè)計(jì)是為了應(yīng)付DOM變動(dòng)頻繁的情況直晨。舉例來(lái)說(shuō),如果在文檔中連續(xù)插入1000個(gè)段落(p元素),會(huì)連續(xù)觸發(fā)1000個(gè)插入事件勇皇,執(zhí)行每個(gè)事件的回調(diào)函數(shù)罩句,這很可能造成瀏覽器的卡頓;
  5. 而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é)

  1. 通過(guò)setState去更新this.state,不要直接操作this.state惧盹,請(qǐng)把它當(dāng)成不可變的乳幸。
  2. 調(diào)用setState更新this.state不是馬上生效的,它是異步滴钧椰,所以不要天真以為執(zhí)行完setState后this.state就是最新的值了粹断。
  3. 多個(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)

  1. setState可能會(huì)引發(fā)不必要的渲染(renders)
  2. setState無(wú)法完全掌控應(yīng)用中所有組件的狀態(tài)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末晕粪,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子渐裸,更是在濱河造成了極大的恐慌巫湘,老刑警劉巖装悲,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異尚氛,居然都是意外死亡诀诊,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門(mén)阅嘶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)畏梆,“玉大人,你說(shuō)我怎么就攤上這事奈懒。” “怎么了宪巨?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵磷杏,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我捏卓,道長(zhǎng)极祸,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任怠晴,我火速辦了婚禮遥金,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蒜田。我一直安慰自己稿械,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布冲粤。 她就那樣靜靜地躺著美莫,像睡著了一般。 火紅的嫁衣襯著肌膚如雪梯捕。 梳的紋絲不亂的頭發(fā)上厢呵,一...
    開(kāi)封第一講書(shū)人閱讀 49,166評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音傀顾,去河邊找鬼襟铭。 笑死,一個(gè)胖子當(dāng)著我的面吹牛短曾,可吹牛的內(nèi)容都是我干的寒砖。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼嫉拐,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼入撒!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起椭岩,我...
    開(kāi)封第一講書(shū)人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤茅逮,失蹤者是張志新(化名)和其女友劉穎璃赡,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體献雅,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡碉考,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了挺身。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片侯谁。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖章钾,靈堂內(nèi)的尸體忽然破棺而出墙贱,到底是詐尸還是另有隱情,我是刑警寧澤贱傀,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布惨撇,位于F島的核電站,受9級(jí)特大地震影響府寒,放射性物質(zhì)發(fā)生泄漏魁衙。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一株搔、第九天 我趴在偏房一處隱蔽的房頂上張望剖淀。 院中可真熱鬧,春花似錦纤房、人聲如沸纵隔。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)巨朦。三九已至,卻和暖如春剑令,著一層夾襖步出監(jiān)牢的瞬間糊啡,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工吁津, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留棚蓄,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓碍脏,卻偏偏與公主長(zhǎng)得像梭依,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子典尾,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容