深刻源碼剖析componentWillXXX爲何UNSAFE

v16.3.0開始以下三個生命週期鉤子被標記爲UNSAFEhtml

  • componentWillMountreact

  • componentWillRecievePropsgit

  • componentWillUpdategithub

究其緣由,有以下兩點:web

  • 這三個鉤子常常被錯誤使用,而且如今出現了更好的替代方案(這裏指新增的getDerivedStateFromPropsgetSnapshotBeforeUpdate)。微信

  • ReactLegacy模式遷移到Concurrent模式後,這些鉤子的表現會和以前不一致。數據結構

本文會從React源碼的角度剖析這兩點。異步

同時,經過本文的學習你能夠掌握React異步狀態更新機制的原理。編輯器

被誤用的鉤子

咱們先來探討第一點,這裏咱們以componentWillRecieveProps舉例。學習

咱們常常在componentWillRecieveProps內處理props改變帶來的影響。有些同窗認爲這個鉤子會在每次props變化後觸發。

真的是這樣麼?讓咱們看看源碼。

這段代碼出自updateClassInstance方法:

if (
 unresolvedOldProps !== unresolvedNewProps ||  oldContext !== nextContext ) {  callComponentWillReceiveProps(  workInProgress,  instance,  newProps,  nextContext,  ); } 複製代碼

你能夠從這裏看到這段源碼

其中callComponentWillReceiveProps方法會調用componentWillRecieveProps

能夠看到,是否調用的關鍵是比較unresolvedOldPropsunresolvedNewProps是否全等,以及context是否變化。

其中unresolvedOldProps爲組件上次更新時的props,而unresolvedNewProps則來自ClassComponent調用this.render返回的JSX中的props參數。

可見他們的引用是不一樣的。因此他們全等比較false

基於此緣由,每次父組件更新都會觸發當前組件的componentWillRecieveProps

想一想你是否也曾誤用過?

模式遷移

讓咱們再看第二個緣由:

ReactLegacy模式遷移到Concurrent模式後,這些鉤子的表現會和以前不一致。

咱們先了解下什麼是模式?不一樣模式有什麼區別?

從Legacy到Concurrent

React15升級爲React16後,源碼改動如此之大,說React被重構可能更貼切些。

正是因爲變更如此之大,使得一些特性在新舊版本React中表現不一致,這裏就包括上文談到的三個生命週期鉤子。

爲了讓開發者能平穩從舊版本遷移到新版本,React推出了三個模式:

  • legacy模式 -- 經過 ReactDOM.render建立的應用會開啓該模式。這是當前 React使用的方式。這個模式可能不支持一些新功能。
  • blocking模式 -- 經過 ReactDOM.createBlockingRoot建立的應用會開啓該模式。開啓部分 concurrent模式特性,做爲遷移到 concurrent模式的第一步。
  • concurrent模式 -- 經過 ReactDOM.createRoot建立的應用會開啓該模式。面向將來的開發模式。

你能夠從這裏看到不一樣模式的特性支持狀況

concurrent模式相較咱們當前使用的legacy模式最主要的區別是將同步的更新機制重構爲異步可中斷的更新

接下來咱們來探討React如何實現異步更新,以及爲何異步更新狀況下鉤子的表現和同步更新不一樣。

同步更新

咱們能夠用代碼版本控制類比更新機制

在沒有代碼版本控制前,咱們在代碼中逐步疊加功能。一切看起來井井有理,直到咱們遇到了一個緊急線上bug(紅色節點)。

爲了修復這個bug,咱們須要首先將以前的代碼提交。

React中,全部經過ReactDOM.render建立的應用都是經過相似的方式更新狀態。

即全部更新同步執行,沒有優先級概念,新來的高優更新(紅色節點)也須要排在其餘更新後面執行。

異步更新

當有了代碼版本控制,有緊急線上bug須要修復時,咱們暫存當前分支的修改,在master分支修復bug並緊急上線。

bug修復上線後經過git rebase命令和開發分支鏈接上。開發分支基於修復bug的版本繼續開發。

React中,經過ReactDOM.createBlockingRootReactDOM.createRoot建立的應用在任務未過時狀況下會採用異步的方式更新狀態。

高優更新(紅色節點)中斷正在進行中的低優更新(藍色節點),先完成渲染流程。

高優更新完成後,低優更新基於高優更新部分或者完整結果從新更新。

深刻源碼

React源碼中,每次發起更新都會建立一個Update對象,同一組件的多個Update(如上圖所示的A -> B -> C)會以鏈表的形式保存在updateQueue中。

首先了解下他們的數據結構

Update有不少字段,當前咱們關注以下三個字段:

const update: Update<*> = {
 // ...省略當前不須要關注的字段  lane,  payload: null,  next: null }; 複製代碼

UpdatecreateUpdate方法返回,你能夠從這裏看到createUpdate的源碼

  • lane:表明優先級。即圖中 紅色節點與 藍色節點的區別。
  • payload:更新掛載的數據。對於 this.setState建立的 更新payloadthis.setState的傳參。
  • next:與其餘 Update鏈接造成鏈表。

updateQueue結構以下:

const queue: UpdateQueue<State> = {
 baseState: fiber.memoizedState,  firstBaseUpdate: null,  lastBaseUpdate: null,  shared: {  pending: null,  },  // 其餘參數省略... }; 複製代碼

UpdateQueueinitializeUpdateQueue方法返回,你能夠從這裏看到initializeUpdateQueue的源碼

  • baseState更新基於哪一個 state開始。上圖中 版本控制的例子中,高優bug修復後提交 master,其餘 commit基於 master分支繼續開發。這裏的 master分支就是 baseState
  • firstBaseUpdatelastBaseUpdate更新基於哪一個 Update開始,由 firstBaseUpdate開始到 lastBaseUpdate結束造成鏈表。這些 Update是在上次 更新中因爲 優先級不夠被留下的,如圖中 A B C
  • shared.pending:本次更新的單或多個 Update造成的鏈表。

其中baseUpdate + shared.pending會做爲本次更新須要執行的Update

例子

瞭解了數據結構,接下來咱們模擬一次異步中斷更新,來揭示本文探尋的祕密 —— componentWillXXX爲何UNSAFE

在某個組件updateQueue中存在四個Update,其中字母表明該Update要更新的字母,數字表明該Update的優先級,數字越小優先級越高。

baseState = '';
 A1 - B2 - C1 - D2 複製代碼

首次渲染時,優先級1。B D優先級不夠被跳過。

爲了保證更新的連貫性,第一個被跳過的UpdateB)及其後面全部Update會做爲第二次渲染的baseUpdate,不管他們的優先級高低,這裏爲B C D

baseState: ''
Updates: [A1, C1] Result state: 'AC' 複製代碼

接着第二次渲染,優先級2。

因爲B在第一次渲染時被跳過,因此在他以後的C形成的渲染結果不會體如今第二次渲染的baseState中。因此baseStateA而不是上次渲染的Result state AC。這也是爲了保證更新的連貫性。

baseState: 'A'          
Updates: [B2, C1, D2] Result state: 'ABCD' 複製代碼

咱們發現,C同時出如今兩次渲染的Updates中,他表明的狀態會被更新兩次。

若是有相似的代碼:

componentWillReceiveProps(nextProps) {
 if (!this.props.includes('C') && nextProps.includes('C')) {  // ...do something  } } 複製代碼

則頗有可能被調用兩次,這與同步更新React表現不一致!

基於以上緣由,componentWillXXX被標記爲UNSAFE

總結

因爲篇幅有限,本次咱們只聚焦了React源碼的冰山一角。

若是想深刻學習React源碼,在此向你推薦開源嚴謹易懂的React源碼電子書 —— React技術揭祕

同時能夠加微信(iamkasong)拉你進源碼交流羣和小夥伴們一塊兒交流React源碼

React Contributor在線答疑。

相關文章
相關標籤/搜索