react 生命周期執行順序,render執行條件

轉載:https://segmentfault.com/a/1190000016617400

首先來列舉一下react的生命周期(react16版本)

Mounting(加載階段:涉及4個鉤子函數)

1.constructor()
加載的時候調用一次,可以初始化state

2.static getDerivedStateFromProps(props, state)
組件每次被rerender的時候,包括在組件構建之后(虛擬dom之后,實際dom掛載之前),每次獲取新的props或state之后;每次接收新的props之后都會返回一個對象作為新的state,返回null則說明不需要更新state;配合componentDidUpdate,可以覆蓋componentWillReceiveProps的所有用法

3.render()
react最重要的步驟,創建虛擬dom,進行diff算法,更新dom樹都在此進行

4.componentDidMount()
組件渲染之后調用,只調用一次

Updating(更新階段:涉及5個鉤子函數)

1.static getDerivedStateFromProps(props, state)
組件每次被rerender的時候,包括在組件構建之后(虛擬dom之后,實際dom掛載之前),每次獲取新的props或state之后;每次接收新的props之后都會返回一個對象作為新的state,返回null則說明不需要更新state;配合componentDidUpdate,可以覆蓋componentWillReceiveProps的所有用法

2.shouldComponentUpdate(nextProps, nextState)
組件接收到新的props或者state時調用,return true就會更新dom(使用diff算法更新),return false能阻止更新(不調用render)

3.render()
react最重要的步驟,創建虛擬dom,進行diff算法,更新dom樹都在此進行

4.getSnapshotBeforeUpdate(prevProps, prevState)
觸發時間: update發生的時候,在render之后,在組件dom渲染之前;返回一個值,作為componentDidUpdate的第三個參數;配合componentDidUpdate, 可以覆蓋componentWillUpdate的所有用法

5.componentDidUpdate()
組件加載時不調用,組件更新完成后調用

Unmounting(卸載階段:涉及1個鉤子函數)
1.componentWillUnmount()

Error Handling(錯誤處理)
1.componentDidCatch(error,info)
任何一處的javascript報錯會觸發

組件的基本寫法

import React, { Component } from 'react'

export default class NewReactComponent extends Component {
    constructor(props) {
        super(props)
        // getDefaultProps:接收初始props
        // getInitialState:初始化state
    }
    state = {

    }
    static getDerivedStateFromProps(props, state) { // 組件每次被rerender的時候,包括在組件構建之后(虛擬dom之后,實際dom掛載之前),每次獲取新的props或state之后;;每次接收新的props之后都會返回一個對象作為新的state,返回null則說明不需要更新state
        return state
    }
    componentDidCatch(error, info) { // 獲取到javascript錯誤

    }
    render() {
        return (
            <h2>New React.Component</h2>
        )
    }
    componentDidMount() { // 掛載后
        
    }   
    shouldComponentUpdate(nextProps, nextState) { // 組件Props或者state改變時觸發,true:更新,false:不更新
        return true
    }
    getSnapshotBeforeUpdate(prevProps, prevState) { // 組件更新前觸發
         return null;
    }
    componentDidUpdate() { // 組件更新后觸發

    }
    componentWillUnmount() { // 組件卸載時觸發

    }
}

新的生命周期

2103351222-5bbb1369e1f41_articlex.jpg

1.React16新的生命周期棄用了componentWillMount、componentWillReceiveProps,componentWillUpdate
2.新增了getDerivedStateFromProps、getSnapshotBeforeUpdate來代替棄用的三個鉤子函數(componentWillMount、componentWillReceiveProps,componentWillUpdate)
3.React16并沒有刪除這三個鉤子函數,但是不能和新增的鉤子函數(getDerivedStateFromProps、getSnapshotBeforeUpdate)混用,React17將會刪除componentWillMount、componentWillReceiveProps,componentWillUpdate
4.新增了對錯誤的處理(componentDidCatch)

了解了生命周期,下面來說一說生命周期的執行順序

1.組件生命周期的執行次數

1.只執行一次: constructor、componentDidMount
2.執行多次:render、getDerivedStateFromProps、shouldComponentUpdate、getSnapshotBeforeUpdate、componentDidUpdate
3.有條件執行:componentWillUnmount

1.組件的生命周期執行順序

假設組件嵌套關系parent組件中有child組件

不更新dom執行順序如下

1602223811(1).jpg

更新dom執行順序如下

1602213835(1).jpg

修改父組件的state

1602214634(1).jpg

修改子組件的state

1602214666(1).jpg

結論

1.完成前的順序是從根部到子部,完成時是從子部到根部。(類似于事件機制)
2.子組件setState是不能觸發其父組件的生命周期更新函數,只能觸發更低一級別的生命周期更新函數。

setState在生命周期中的使用注意事項
1、僅當子組件的props發生變化時getDerivedStateFromProps生命鉤子才會被觸發。該生命周期會有一個參數nextProps,表示子組件被更新后的props。因此可以在該周期獲取最新的props在通過setState更新組件狀態。

2、子組件props或state更新都會觸發shouldComponentUpdate生命鉤子。該生命周期有兩個參數nextProps,nextState 表示更新后的props和更新后的state, 該生命周期是整提高組件性能的一個重要函數,它通過判斷當前狀態與之前狀態來返回一個布爾值并決定是否更新視圖,如果返回false視圖始終不會更新。返回true就會更新視圖

3、getSnapshotBeforeUpdate生命周期在shouldComponentUpdate返回true后被觸發。在這兩個生命周期只要視圖更新就會觸發,因此不能再這兩個生命周期中使用setState。否則會導致死循環。

4、componentDidUpdate生命周期在shouldComponentUpdate返回true后觸發。在此生命周期中setState會導致視圖再次更新,類似于componentDidMount,因此除非特殊業務需求,否則不建議在此生命周期中使用setState。

5、componentWillUnmount生命周期在組件被卸載后觸發,在此生命周期使用setState不會觸發。

以下文章來源于魔術師卡頌 ,作者卡頌

react render渲染條件

點擊Parent組件的div,觸發更新,Son組件會打印child render!么?

function Son() {
  console.log('child render!');
  return <div>Son</div>;
}


function Parent(props) {
  const [count, setCount] = React.useState(0);

  return (
    <div onClick={() => {setCount(count + 1)}}>
      count:{count}
      {props.children}
    </div>
  );
}


function App() {
  return (
    <Parent>
      <Son/>
    </Parent>
  );
}

const rootEl = document.querySelector("#root");
ReactDOM.render(<App/>, rootEl);

render需要滿足的條件

React創建Fiber樹時,每個組件對應的fiber都是通過如下兩個邏輯之一創建的:
render。即調用render函數,根據返回的JSX創建新的fiber。
bailout。即滿足一定條件時,React判斷該組件在更新前后沒有發生變化,則復用該組件在上一次更新的fiber作為本次更新的fiber。
可以看到,當命中bailout邏輯時,是不會調用render函數的。
所以,Son組件不會打印child render!是因為命中了bailout邏輯。

bailout需要滿足的條件

什么情況下會進入bailout邏輯?當同時滿足如下4個條件時:

  1. oldProps === newProps ?

即本次更新的props(newProps)不等于上次更新的props(oldProps)。
注意這里是全等比較。
我們知道組件render會返回JSX,JSX是React.createElement的語法糖。
所以render的返回結果實際上是React.createElement的執行結果,即一個包含props屬性的對象。
即使本次更新與上次更新props中每一項參數都沒有變化,但是本次更新是React.createElement的執行結果,是一個全新的props引用,所以oldProps !== newProps。
如果我們使用了PureComponent或Memo,那么在判斷是進入render還是bailout時,不會判斷oldProps與newProps是否全等,而是會對props內每個屬性進行淺比較。

  1. context沒有變化

即context的value沒有變化。

  1. workInProgress.type === current.type ?

更新前后fiber.type是否變化,比如div是否變為p。

  1. !includesSomeLane(renderLanes, updateLanes) ?

當前fiber上是否存在更新,如果存在那么更新的優先級是否和本次整棵fiber樹調度的優先級一致?
如果一致則進入render邏輯。
就我們的Demo來說,Parent是整棵樹中唯一能觸發更新的組件(通過調用setCount)。
所以Parent對應的fiber是唯一滿足條件4的fiber。

Demo的詳細執行邏輯

所以,Demo中Son進入bailout邏輯,一定是同時滿足以上4個條件。我們一個個來看。
條件2,Demo中沒有用到context,滿足。
條件3,更新前后type都為Son對應的函數組件,滿足。
條件4,Son本身無法觸發更新,滿足。
所以,重點是條件1。讓我們詳細來看下。
本次更新開始時,Fiber樹存在如下2個fiber

FiberRootNode
      |
  RootFiber

其中FiberRootNode是整個應用的根節點,RootFiber是調用ReactDOM.render創建的fiber。
首先,RootFiber會進入bailout的邏輯,所以返回的App fiber和更新前是一致的。

FiberRootNode
      |
  RootFiber      
      |
  App fiber

由于App fiber是RootFiber走bailout邏輯返回的,所以對于App fiber,oldProps === newProps。并且bailout剩下3個條件也滿足。
所以App fiber也會走bailout邏輯,返回Parent fiber。

FiberRootNode
      |
  RootFiber      
      |
   App fiber
      |
 Parent fiber

由于更新是Parent fiber觸發的,所以他不滿足條件4,會走render的邏輯。
接下來是關鍵
如果render返回的Son是如下形式:

<Son/>

會編譯為

React.createElement(Son, null)

執行后返回JSX。
由于props的引用改變,oldProps !== newProps。會走render邏輯。
但是在Demo中Son是如下形式:

{props.children}

其中,props.children是Son對應的JSX,而這里的props是App fiber走bailout邏輯后返回的。
所以Son對應的JSX與上次更新時一致,JSX中保存的props也就一致,滿足條件1。
可以看到,Son滿足bailout的所有條件,所以不會render。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,563評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,694評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,672評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,965評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,690評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,019評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,013評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,188評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,718評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,438評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,667評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,149評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,845評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,252評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,590評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,384評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,635評論 2 380