react生命周期更新前后的知識(shí)整理

對(duì)于react生命周期的理解,反反復(fù)復(fù)有很多次不同的理解狭握,我就做個(gè)整理递览,以免每次都進(jìn)行重新推翻
按照官網(wǎng)的解釋組件的生命周期分成掛載叼屠,更新,卸載绞铃,以及錯(cuò)誤處理的幾個(gè)流程
16.3以前的生命周期分成

  • 初始化階段constuctor,
  • 掛載階段有componentWillMount,render ,componentDidMount
  • 更新階段的流程是componentWillReceiveProps镜雨,shouldComponentUpdate,componentWillUpdate儿捧, render,componentDidUpdate
  • 卸載 階段componentWillunMount
  • 錯(cuò)誤處理 componentDidCatch()
    16.4推出fibber之后菲盾,官網(wǎng)也說將在17.0開啟async rendering(異步渲染)
    那么render之前的函數(shù)都將被執(zhí)行多次 所以
    16.3以后新增getDerivedStateFromProp將逐漸替代render之前(除shouldComponentUpdate)的生命(componentWillMount,componentWillUpdate, componentWillReceiveProps)

componentWillMount

該生命是在組件掛載到dom之前會(huì)被調(diào)用 只調(diào)用一次,官網(wǎng)指明在這里用setstate不會(huì)引起組件重新渲染dom懒鉴,此方法是服務(wù)端渲染唯一會(huì)調(diào)用的生命周期函數(shù)

(我試了下在componentsWillMount直接調(diào)用setState。是可以讓渲染后拿到最新的state的值临谱,但是此時(shí)render只調(diào)用一次。就說明這里用setstate不會(huì)引起組件重新(第二次)渲染dom悉默。只是在render之前setState已經(jīng)將需要更新的加入隊(duì)列了。)這樣不算重新觸發(fā)渲染的更新是沒什么意義抄课,而這樣的初始化state應(yīng)該放在constuctor里面

componentDiDMount

組件掛載后唱星,(插入到Dom樹)之后會(huì)被調(diào)用跟磨。官網(wǎng)建議網(wǎng)絡(luò)數(shù)據(jù)請(qǐng)求,最適合放在這里吱晒。依賴dom節(jié)點(diǎn)的初始化應(yīng)該放在這個(gè)生命周期
但是不適合直接在這里調(diào)用setState(),因?yàn)閏omponentDidMount本身處于一次更新中沦童,我們又調(diào)用了一次setstate 就會(huì)在未來在執(zhí)行一次render 造成不必要的性能浪費(fèi)仑濒。所以不推薦直接在關(guān)于componentDidMount調(diào)用setstate叹话。 但是在componentDidMount可以條用接口,在回調(diào)中去修改setstate墩瞳。
官網(wǎng)也指明說驼壶,兩次渲染會(huì)發(fā)生在瀏覽器更新屏幕之前,但是不推薦喉酌。會(huì)導(dǎo)致性能問題热凹。

關(guān)于在哪個(gè)生命周期發(fā)起異步請(qǐng)求獲取頁面初始數(shù)據(jù)

那如果在這里發(fā)送異步請(qǐng)求拉去數(shù)據(jù)并且setState更新數(shù)據(jù)呢泪电,是不是可以比在componentDidMount減少一次渲染般妙,然后優(yōu)先提早拿到更新的數(shù)據(jù)呢?(官網(wǎng)不推薦)

  state={
    count:0
  }
 componentWillMount(){
    console.log('willMount')
    fetch('s.codepen.io')
    .then(res =>{ 
      this.setState({count: 'success'})
      console.log('setdata')
    })
    .catch(err => this.setState({count: 'error'}))
  }
 componentDidMount(){
    console.log('didMount')
  }
render() {
  console.log('render')
  return <div>{this.state.count}</div>
}
//頁面最后顯示success 打印結(jié)果
// willMount
// render
// didMount
//setdata
//render

可以看出相速,render是在componentWillMount執(zhí)行之后馬上就被調(diào)用碟渺,所以此時(shí)由于異步請(qǐng)求還沒有拿到數(shù)據(jù)。等到異步請(qǐng)求拿到數(shù)據(jù)之后去setState突诬。會(huì)重新調(diào)用render,總的來說還是進(jìn)行了兩次渲染苫拍,異步請(qǐng)求之后的setState還是觸發(fā)了渲染更新。所以初始需要請(qǐng)求異步數(shù)據(jù)旺隙,放在這里也同樣需要render一次“加載中”的空數(shù)據(jù)狀態(tài)绒极。總的來說蔬捷,組件在首次渲染時(shí)總是會(huì)處于沒有異步數(shù)據(jù)的狀態(tài)垄提。

那么為什么建議在componentDiDMount異步獲取外部數(shù)據(jù)呢?

1抠刺、如果是服務(wù)端渲染塔淤,componentWillMount是唯一會(huì)執(zhí)行的生命周期,如果是服務(wù)端渲染速妖,在這里獲取數(shù)據(jù)(發(fā)送請(qǐng)求)可能會(huì)執(zhí)行兩次高蜂。一次是在服務(wù)端一次是在客戶端
2、如果在16.4之后增加了fiber罕容,使的整個(gè)React的生命周期分成兩個(gè)階段备恤,在第一階段的生命周期是可以被中斷的,每次中斷之后都會(huì)重新執(zhí)行第一階段得锦秒,而第二階段不能中斷露泊。一旦觸發(fā)第二階段,就一定要等到第二階段執(zhí)行完畢旅择,componentWillMount在第一階段惭笑,componentDidMount在第二階段,如果吧請(qǐng)求放在componentWillMount中則可能發(fā)送多次請(qǐng)求,

綜上所述所以放在componentDidMount中更合適

  • 關(guān)于事件訂閱
    一般情況下沉噩,如果在componentWillMount 中做訂閱外部事件捺宗,會(huì)在componentWillunMount中取消訂閱蚜厉,但是在服務(wù)端渲染的情況下畜眨,服務(wù)端是不會(huì)調(diào)用componentWillunMount,所以在服務(wù)端訂閱事件是會(huì)導(dǎo)致內(nèi)存泄露贰健。
    另一方面聽上面的問題一樣早抠。在未來開啟React異步渲染之后,第一階段componentWillMount調(diào)用之后悬垃,組件的渲染還是有可能被其他事物中斷的甘苍,所以沒有辦法保證componentWillunMount可以被調(diào)用 。看彼。
    componentDidMount不會(huì)有這個(gè)問題 所以添加訂閱也應(yīng)該在componentDidMount中

componentWillReceiveProps

componentWillReceiveProps(nextProps)
調(diào)用時(shí)機(jī):只有在父組件重新渲染的時(shí)候(就是已掛載的組件接收到新的props之前靖榕,(此時(shí)this.props訪問到的還是渲染之前的props))調(diào)用顽铸,不管父組件傳來的props有沒有改變。只要父組件重新渲染都會(huì)調(diào)用此方法谓松、

getDerivedStateFromProps

getDerivedStateFromProps(props,state)
是靜態(tài)方法,無權(quán)訪問組件實(shí)例娜膘,(即使無法使用this)在state更新或者props更新的時(shí)候都會(huì)調(diào)用組件竣贪,就是每次渲染前都會(huì)調(diào)用,這個(gè)與componentWillReceiveProps不同贾富。此方法適用于罕見案例,就是state的值在任何情況下都去取決于props。返回一個(gè)對(duì)象用來更新state淑际,返回null不更新任何內(nèi)容

需要優(yōu)化的點(diǎn):

  • 基于 props 更新 state
    一般如果是組件的state的值任何情況下都依賴于props的時(shí)候春缕,在16.3以后應(yīng)該拋棄componentWillReceiveProps,而使用getDerivedStateFromProps返回一個(gè)對(duì)象用來更新state.
static getDerivedStateFromProps(props, state) {
    if (props.currentRow !== state.lastRow) {
      return {
        isScrollingDown: props.currentRow > state.lastRow,
        lastRow: props.currentRow,
      };
    }

    // 返回 null 表示無需更新 state票灰。
    return null;
  }
  • props 更新時(shí)獲取外部數(shù)據(jù)宅荤,props 更新的副作用
    如果需要更新狀態(tài)以響應(yīng)props的更改,則可以通過用this.props 和nextProps進(jìn)行比較惹盼。在掛載的過程中手报,不會(huì)針對(duì)初始的props去調(diào)用改方法改化,

官方指出,如果要執(zhí)行副作用(數(shù)據(jù)提取和動(dòng)畫)請(qǐng)改用componentDidUpdate 揍鸟,在這之前很多時(shí)候都會(huì)用到redux存放props燥爷。如果有props更新引起的副作用。所以就會(huì)有

之前16.3之前大多數(shù)用
class ExampleComponent extends React.Component {
  componentWillReceiveProps(nextProps) {
    if (this.props.isVisible !== nextProps.isVisible) {
      this._loadAsyncData(nextProps.isVisible);
    }
  }
}
 static getDerivedStateFromProps(props, state) {
    // 保存 prevId 在 state 中稚配,以便我們?cè)?props 變化時(shí)進(jìn)行對(duì)比道川。
    // 清除之前加載的數(shù)據(jù)(這樣我們就不會(huì)渲染舊的內(nèi)容)。
    if (props.id !== state.prevId) {
      return {
        externalData: null,
        prevId: props.id,
      };
    }
    // 無需更新 state
    return null;
  }
 componentDidUpdate(prevProps, prevState) {
    if (this.state.externalData === null) {
      this._loadAsyncData(this.props.id);
    }
  }

與 componentWillUpdate 類似臊岸,componentWillReceiveProps 可能在一次更新中被多次調(diào)用尊流,也就是說寫在這里的副作用方法,異步請(qǐng)求逻住,回調(diào)函數(shù)也有可能會(huì)被調(diào)用多次瞎访,而此時(shí)與 componentDidMount 類似吁恍,componentDidUpdate 也不存在這樣的問題,一次更新中 componentDidUpdate 只會(huì)被調(diào)用一次伴奥,所以官網(wǎng)建議講 componentDidUpdate 就可以解決這個(gè)問題

  • 同樣的props更新引起的副作用也應(yīng)該從componentWillReceiveProps 遷移到componentDidUpdate
  • 以及props更新引起的調(diào)用外部回調(diào)咕幻。也應(yīng)該從componentWillUpdate遷移至componentDidUpdate

getSnapshotBeforeUpdate

getSnapshotBeforeUpdate(prevProps, prevState)
調(diào)用時(shí)機(jī):會(huì)在最終的render之前被調(diào)用肄程,也就是getSnapshotBeforeUpdate中獲取到的dom元素狀態(tài)與componentWillUpdate的是一樣的,所以可以用 這個(gè)方法代替componentWillUpdate獲取組件更改之前捕獲一些dom信息(例如:滾動(dòng)高度玄叠、)
返回的一個(gè)值作為componentDidUpdate, 的第三個(gè)參數(shù)拓提。
配個(gè)componentDidUpdate。覆蓋componentWillUpdate的用法

-優(yōu)化的點(diǎn):在更新前記錄獲取原來的dom節(jié)點(diǎn)屬性
在沒有這個(gè)生命周期之前寺惫,一般會(huì)利用在componentWillUpdate讀取更新前dom元素狀態(tài)屬性蹦疑,但是在異步渲染中,render階段的生命周期(如 componentWillUpdate 和 render)和commoit階段的生命周期“”(componentDidUpdate)可能存在延遲艇肴、
官方提供了下面這個(gè)例子

class ScrollingList extends React.Component {
  constructor(props) {
    super(props);
    this.listRef = React.createRef();
  }

  getSnapshotBeforeUpdate(prevProps, prevState) {
    // 我們是否在 list 中添加新的 items ?
    // 捕獲滾動(dòng)??位置以便我們稍后調(diào)整滾動(dòng)位置核畴。
    if (prevProps.list.length < this.props.list.length) {
      const list = this.listRef.current;
      return list.scrollHeight - list.scrollTop;
    }
    return null;
  }

  componentDidUpdate(prevProps, prevState, snapshot) {
    // 如果我們 snapshot 有值冲九,說明我們剛剛添加了新的 items,
    // 調(diào)整滾動(dòng)位置使得這些新 items 不會(huì)將舊的 items 推出視圖咖刃。
    //(這里的 snapshot 是 getSnapshotBeforeUpdate 的返回值)
    if (snapshot !== null) {
      const list = this.listRef.current;
      list.scrollTop = list.scrollHeight - snapshot;
    }
  }

  render() {
    return (
      <div ref={this.listRef}>{/* ...contents... */}</div>
    );
  }
}

參考官方文檔
[https://react.docschina.org/blog/2018/03/27/update-on-async-rendering.html#fetching-external-data]

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市花鹅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌古拴,老刑警劉巖黄痪,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件挺尾,死亡現(xiàn)場離奇詭異恢准,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)麻顶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門矫钓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來概龄,“玉大人衰粹,你說我怎么就攤上這事瓢捉∈蘖蓿” “怎么了拍摇?”我有些...
    開封第一講書人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長宿接。 經(jīng)常有香客問我睦霎,道長,這世上最難降的妖魔是什么副女? 我笑而不...
    開封第一講書人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮几蜻,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘位隶。我一直安慰自己开皿,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開白布笋妥。 她就那樣靜靜地躺著窄潭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪月帝。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,443評(píng)論 1 302
  • 那天嚷辅,我揣著相機(jī)與錄音簸搞,去河邊找鬼。 笑死域仇,一個(gè)胖子當(dāng)著我的面吹牛则酝,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播沽讹,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼爽雄,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了叹谁?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤焰檩,失蹤者是張志新(化名)和其女友劉穎析苫,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體穿扳,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年茫死,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片峦萎。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡骨杂,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出搓蚪,到底是詐尸還是另有隱情丁鹉,我是刑警寧澤悴能,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布漠酿,位于F島的核電站谎亩,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏夫凸。R本人自食惡果不足惜阱持,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望鸽扁。 院中可真熱鬧镶骗,春花似錦、人聲如沸巩那。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽跺嗽。三九已至,卻和暖如春植兰,著一層夾襖步出監(jiān)牢的瞬間楣导,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來泰國打工筒繁, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人毡咏。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像堵泽,于是被迫代替她去往敵國和親恢总。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354