setState 簡(jiǎn)單整理

好久沒寫東西了淮蜈。。已卷。梧田。
來(lái)頭條之后, 每天身體被掏空侧蘸。也沒多少時(shí)間寫東西裁眯,
今天周末,上午沒啥事讳癌, 簡(jiǎn)單寫寫穿稳,


一個(gè)例子

假如有這樣一個(gè)點(diǎn)擊執(zhí)行累加場(chǎng)景:

// ...
  this.state = {
    count: 0,
  }

  incrementCount() {
    this.setState({count: this.state.count + 1});
  }

  handleIncrement = () => {
    this.incrementCount();
    this.incrementCount();
    this.incrementCount();
  }


 <button ref="button" onClick={this.handleIncrement}>點(diǎn)擊</button>
// ...

每一次點(diǎn)擊晌坤, 會(huì)執(zhí)行三次累加逢艘,看一下輸入:


image.png

并沒有達(dá)到預(yù)期的效果旦袋,糾正也很簡(jiǎn)單:

incrementCount() {
  this.setState((prevState) => {
    return {count: prevState.count + 1}
  });
}

再看輸出:


image.png

setState 的時(shí)候, 一個(gè)傳入了object, 一個(gè)傳入了更新函數(shù)埋虹。
區(qū)別在于: 傳入一個(gè)更新函數(shù)猜憎,就可以訪問(wèn)當(dāng)前狀態(tài)值娩怎。 setState調(diào)用是批量處理的搔课,因此可以讓更新建立在彼此之上,避免沖突截亦。

那問(wèn)題來(lái)了爬泥, 為什么前一種方式就不行呢? 具體一點(diǎn)說(shuō)崩瓤, setState為什么不會(huì)同步更新組件狀態(tài)呢袍啡? 帶著這個(gè)疑問(wèn),繼續(xù)往下看却桶。

進(jìn)入這個(gè)問(wèn)題之前境输,我們先回顧一下現(xiàn)在對(duì)setState的認(rèn)知:

  1. setState不會(huì)立刻改變React組件中state的值.
  2. setState通過(guò)觸發(fā)一次組件的更新來(lái)引發(fā)重繪.

重繪指的就是引起React的更新生命周期函數(shù)4個(gè)函數(shù):

  • shouldComponentUpdate(被調(diào)用時(shí)this.state沒有更新;如果返回了false颖系,生命周期被中斷嗅剖,雖然不調(diào)用之后的函數(shù)了,但是state仍然會(huì)被更新)
  • componentWillUpdate(被調(diào)用時(shí)this.state沒有更新)
  • render(被調(diào)用時(shí)this.state得到更新)
  • componentDidUpdate
  1. 多次setState函數(shù)調(diào)用產(chǎn)生的效果會(huì)合并嘁扼。

如果每一次setState調(diào)用都走一圈生命周期,光是想一想也會(huì)覺得會(huì)帶來(lái)性能的問(wèn)題,其實(shí)這四個(gè)函數(shù)都是純函數(shù)球散,性能應(yīng)該還好欣鳖,但是render函數(shù)返回的結(jié)果會(huì)拿去做Virtual DOM比較和更新DOM樹,這個(gè)就比較費(fèi)時(shí)間不傅。

目前React會(huì)將setState的效果放在隊(duì)列中旅掂,積攢著一次引發(fā)更新過(guò)程,為的就是把Virtual DOM和DOM樹操作降到最小访娶,用于提高性能商虐。

查閱一些資料后發(fā)現(xiàn),某些操作還是可以同步更新this.state的震肮。
直接說(shuō)結(jié)論吧:

在React中称龙,如果是由React引發(fā)的事件處理(比如通過(guò)onClick引發(fā)的事件處理),調(diào)用setState不會(huì)同步更新this.state戳晌,除此之外的setState調(diào)用會(huì)同步執(zhí)行this.state鲫尊。

所謂“除此之外”,指的是繞過(guò)React通過(guò)addEventListener直接添加的事件處理函數(shù)沦偎,還有通過(guò)setTimeout/setInterval產(chǎn)生的異步調(diào)用疫向。

https://jsbin.com/mavolejufi/edit?html,js,console,output

盜用一張圖:

setState 過(guò)程

在React的setState函數(shù)實(shí)現(xiàn)中咳蔚,會(huì)根據(jù)一個(gè)變量isBatchingUpdates判斷是直接更新this.state還是放到隊(duì)列中。
而isBatchingUpdates默認(rèn)是false搔驼,也就表示setState會(huì)同步更新this.state谈火,但是有一個(gè)函數(shù)batchedUpdates,這個(gè)函數(shù)會(huì)把isBatchingUpdates修改為true舌涨,而當(dāng)React在調(diào)用事件處理函數(shù)之前就會(huì)調(diào)用這個(gè)batchedUpdates糯耍,造成的后果,就是由React控制的事件處理過(guò)程setState不會(huì)同步更新this.state囊嘉。

知道上面的一些理論之后温技, 我們?cè)倏匆粋€(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;
  }
};

毫無(wú)疑問(wèn)輸出 0 0 2 3 ;

前兩次在isBatchingUpdates 中扭粱,不用更新state, 輸出兩個(gè)0舵鳞。
后面兩次會(huì)同步更新, 分別輸出2琢蛤, 3蜓堕;

上面的例子,我們就知道了setState 是可以同步更新的博其,但是還是盡量避免直接使用套才, 僅作了解用。
如果你非要玩一些騷操作贺奠,寫出這樣的代碼去直接去操作this.state:

    this.state.count = this.state.count + 1;
    this.state.count = this.state.count + 1;
    this.state.count = this.state.count + 1;
    this.setState();

我只能說(shuō)霜旧, 大胸弟, 你很騷儡率。吾有舊友叼似汝挂据,而今墳草丈許高。

根據(jù)以上內(nèi)容儿普, 簡(jiǎn)單重復(fù)下結(jié)論:

  • 不要直接去操作this.state崎逃, 這樣會(huì)造成不必要的性能更問(wèn)題和隱患。
  • 由React引發(fā)的事件處理眉孩,調(diào)用setState不會(huì)同步更新this.state个绍,除此之外的setState調(diào)用會(huì)同步執(zhí)行this.state。

就簡(jiǎn)單寫到這吧浪汪, 要出門了 :)巴柿。

擴(kuò)展閱讀:
https://reactjs.org/docs/faq-state.html#what-does-setstate-do

關(guān)于事務(wù),還有相關(guān)源碼的問(wèn)題可以參考:
https://zhuanlan.zhihu.com/p/20328570

https://zhuanlan.zhihu.com/p/25882602
https://zhuanlan.zhihu.com/p/26069727
https://zhuanlan.zhihu.com/p/25990883
https://reactjs.org/docs/faq-state.html#what-does-setstate-do
https://reactjs.org/docs/react-component.html#setstate

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末死遭,一起剝皮案震驚了整個(gè)濱河市广恢,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌呀潭,老刑警劉巖钉迷,帶你破解...
    沈念sama閱讀 211,561評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件至非,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡糠聪,警方通過(guò)查閱死者的電腦和手機(jī)荒椭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,218評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)舰蟆,“玉大人趣惠,你說(shuō)我怎么就攤上這事∝裁纾” “怎么了信卡?”我有些...
    開封第一講書人閱讀 157,162評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵隔缀,是天一觀的道長(zhǎng)题造。 經(jīng)常有香客問(wèn)我,道長(zhǎng)猾瘸,這世上最難降的妖魔是什么界赔? 我笑而不...
    開封第一講書人閱讀 56,470評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮牵触,結(jié)果婚禮上淮悼,老公的妹妹穿的比我還像新娘。我一直安慰自己揽思,他們只是感情好袜腥,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,550評(píng)論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著钉汗,像睡著了一般羹令。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上损痰,一...
    開封第一講書人閱讀 49,806評(píng)論 1 290
  • 那天福侈,我揣著相機(jī)與錄音,去河邊找鬼卢未。 笑死肪凛,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的辽社。 我是一名探鬼主播伟墙,決...
    沈念sama閱讀 38,951評(píng)論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼滴铅!你這毒婦竟也來(lái)了戳葵?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,712評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤失息,失蹤者是張志新(化名)和其女友劉穎譬淳,沒想到半個(gè)月后档址,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,166評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡邻梆,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,510評(píng)論 2 327
  • 正文 我和宋清朗相戀三年守伸,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片浦妄。...
    茶點(diǎn)故事閱讀 38,643評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡尼摹,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出剂娄,到底是詐尸還是另有隱情蠢涝,我是刑警寧澤,帶...
    沈念sama閱讀 34,306評(píng)論 4 330
  • 正文 年R本政府宣布阅懦,位于F島的核電站和二,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏耳胎。R本人自食惡果不足惜惯吕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,930評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望怕午。 院中可真熱鬧废登,春花似錦、人聲如沸郁惜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,745評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)兆蕉。三九已至羽戒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間恨樟,已是汗流浹背半醉。 一陣腳步聲響...
    開封第一講書人閱讀 31,983評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留劝术,地道東北人缩多。 一個(gè)月前我還...
    沈念sama閱讀 46,351評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像养晋,于是被迫代替她去往敵國(guó)和親衬吆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,509評(píng)論 2 348

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