React組件到底何時render啊

今天咱們React源碼交流羣裏有個小夥伴提出個有趣的問題,以爲本身對React運行流程理解很到位的同窗,能夠來看看。前端

對於以下Demo,點擊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); 複製代碼

在線Demo地址markdown

👉右滑顯示答案:                                                                                                                                                 不會    
複製代碼

render須要知足的條件

React建立Fiber樹時,每一個組件對應的fiber都是經過以下兩個邏輯之一建立的:函數

  • render。即調用render函數,根據返回的JSX建立新的fiberoop

  • bailout。即知足必定條件時,React判斷該組件在更新先後沒有發生變化,則複用該組件在上一次更新的fiber做爲本次更新的fiberui

能夠看到,當命中bailout邏輯時,是不會調用render函數的。spa

因此,Son組件不會打印child render!是由於命中了bailout邏輯。code

bailout須要知足的條件

什麼狀況下會進入bailout邏輯?當同時知足以下4個條件時:orm

1. oldProps === newProps ?

即本次更新的props(newProps)不等於上次更新的props(oldProps)。xml

注意這裏是全等比較

咱們知道組件render會返回JSXJSXReact.createElement的語法糖。

因此render的返回結果其實是React.createElement的執行結果,即一個包含props屬性的對象。

即便本次更新與上次更新props中每一項參數都沒有變化,可是本次更新是React.createElement的執行結果,是一個全新的props引用,因此oldProps !== newProps

若是咱們使用了PureComponentMemo,那麼在判斷是進入render仍是bailout時,不會判斷oldPropsnewProps是否全等,而是會對props內每一個屬性進行淺比較。

2. context沒有變化

contextvalue沒有變化。

3. workInProgress.type === current.type ?

更新先後fiber.type是否變化,好比div是否變爲p

4. !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 fiberRootFiberbailout邏輯返回的,因此對於App fiberoldProps === 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.childrenSon對應的JSX,而這裏的propsApp fiberbailout邏輯後返回的。

因此Son對應的JSX與上次更新時一致,JSX中保存的props也就一致,知足條件1。

能夠看到,Son知足bailout的全部條件,因此不會render

總結

當你理解這4個條件後,對於React組件更新會有全新的認識。

不得不說,React真是太難了,打工人流下了不爭氣的眼淚。

關注公衆號,進源碼羣,和其餘前端打工人一塊兒成長。

相關文章
相關標籤/搜索