重新了解React中的key (Part 2)

MengWei Chen
5 min readOct 13, 2017

--

React 中的 key 主要是除了搭配Array使用外,也可以用來解決一些元件更新及生命週期的問題,這個主題分成兩篇文章來探討

第一篇(Part 1) 主要是透過範例的方式說明Key值到底要給什麼內容才適當,
若您對Key值還沒有基本認識的話,建議先看一下第一部分

第二篇(Part 2) 也就是本篇,將介紹如何利用自訂Key值的方式解決生命週期的問題(或者是說強迫React重新產生新的Component)

其實Key的使用不只是搭配Array而已,也可以用來解決一些元件更新及生命週期的問題。

注意! 接下來的範例只是提供一個操作上的思考,在真實的開發中並不建議把 fetch 放在component的生命週期內。

更實際的應用場景是在Component內使用非React的Lib,例如文字編輯器、圖片編輯器等,需要在React component中初始化及管理這些套件的應用。

另外透過Key值的修改會觸發React進行unMount的動作,大量且頻繁的操作可能會對效能上產生負擔,這方面需要在使用的時候進行評估。

若想要了解這個做法的動機或是對於效能上覺得有疑慮,請直接跳到補充說明的部分

請參考下面的程式碼,首先我們先宣告一個list並且在render function中將該List映射成div元件,並且綁定click事件,觸發時修改state內的currentItemName。

接下來是InfoPanel組件,該組件會在componentDidMount時根據props傳入的data.url發送request(fetch),收到response時則根據結果將資料顯示出來

整個程式的功能為點擊左側選單,會去fetch資料,取得結果後將資訊顯示在右側。不過很遺憾的,這樣的作法只有在第一次點擊時才有效,原因是當InfoPanel出現之後,即使傳入的props有變化,實際React的動作是 “更新” InfoPanel,並不會觸發InfoPanel內的componentDidMount。

只有第一次點擊有效的版本

這種情況下一般的解決方式為,在InfoPanel內加一個componentWillReceiveProps,去判斷當下的data跟接下來傳入的data是否相同,若不相同則把fetchState設回init ,並重新fetch資料。(如下方所示)

完整範例請參考https://stackblitz.com/edit/react-key-example2-1

但其實我們還有另一種做法,上一篇文章有提到,React會透過key來判斷物件是要更新還是移除/新增。這種狀況下,可以更新InfoPanel 的key,告訴React這個InfoPanel已經不是之前的那個InfoPanel了,請幫重新產生一個新的!

透過這樣的方式(上11行),就可避免在Component內加入的判斷式,不但減少了複雜度跟程式碼行數,更重要的是可以避免一些關於更新的Bug產生。

透過給定key值的解決方案

結論

再重複一次Part 1的小結

React在render時若無使用者自訂的key值,則依照同層級中定義的順序當key值。
在React v15以前的版本,若同層級中出現了兩個一樣的key值,則會忽略後來出現的Component。

React在實際修改DOM之前,會比較前一次render跟目前這一次render結果的“差異”,同一個層級中的所有Component

若出現key值一樣但props不一樣的Component則進行更新

若比對所有key值後,發現有之前不存在的key值,則新增

若比對所有key值後,發現有之前存在的key值消失,則刪除

掌握了這幾個要點,往後開發遇到了組建預期更新但沒更新,或是要強迫React把整個元件的生命週期重跑一遍,就可以輕鬆的使用Key來解決~

補充說明

會有想要透過Key去強迫重新產生Component的理由是,假設今天我是一個筆記軟體,左邊是筆記清單,右邊是編輯器。切換筆記這件事情,對於右邊的編輯器來說,與其去透過componentWillReceiveProps去重新初始化我的第三方編輯器套件,及初始化我的Component State,那不如就把整個Component unMount掉,產生一個新的來處理。

這樣的做法很多人會開始思考效能問題,但其實有一個大家很常用的React library就是一直在做類似的事情(把元件mount 跟 unMount),那就是react-router。我們常會用react-router去切換不同的“頁面元件“,其實我們只要把“用Key值修改強迫更新”這件事想成透過用router更新“頁面元件“,好像就也沒有想像中的可怕。

最後我要說的是這篇文章想要帶給各位的是一個想法跟思路,要不要用這樣的方式,其實就是一個想法跟取捨的問題。

另外這個做法並非只有我一個人提出,對岸的掏寶前端部落格也有一致的想法,強調要給元件一個“健康的生命週期”。

--

--