天天看點

17. 精讀《如何安全地使用 React context》

本期精讀文章是:How to safely use React context

1 引言

在 React 源碼中,context 始終存在,卻在 React 0.14 的官方文檔中才有所展現。在目前最新的官方文檔中,仍不建議使用 context,也表明 context 是一個實驗性的 API,在未來 React 版本中可能被更改。那麼哪些場景下需要用到 context,而哪些情況下應該避免使用,context 又有什麼坑呢?讓我們一起來讨論一下。

2 内容概要

React context 可以把資料直接傳遞給元件樹的底層元件,而無需中間元件的參與。Redux 作者 Dan Abramov 為 contenxt 的使用總結了一些注意事項:

  • 如果你是一個庫的作者,需要将資訊傳遞給深層次元件時,context 在一些情況下可能無法更新成功。
  • 如果是界面主題、本地化資訊,context 被應用于不易改變的全局變量,可以提供一個高階元件,以便在 API 更新時隻需修改一處。
  • 如果庫需要你使用 context,請它提供高階元件給你。

正如 Dan 第一條所述,在 React issue 中,經常能找到 React.PureComponent、shouldComponentUpdate 與包含 Context 的庫結合後引發的一些問題。原因在于 shouldComponentUpdate 會切斷子樹的 rerender,當 state 或 props 沒有發生變化時,可能意外中斷上層 context 傳播。也就是當 shouldComponentUpdate 傳回 false 時,context 的變化是無法被底層所感覺的。

是以,我們認為 context 應該是不變的,在構造時隻接受 context 一次,使用 context,應類似于依賴注入系統來進行。結合精讀文章的示例總結一下思路,不變的 context 中包含可變的元素,元素的變化觸發自身的監聽器實作底層元件的更新,進而繞過 shouldComponentUpdate。

最後作者提出了 Mobx 下的兩種解決方案。context 中的可變元素可用 observable 來實作,進而避免上述事件監聽器編寫,因為 observable 會幫你完成元素改變後的響應。當然 Provider + inject 也可以完成,具體可參考精讀文章中的代碼。

3 精讀

本次提出獨到觀點的同學有: @monkingxue @alcat2008 @ascoders,精讀由此歸納。

context 的使用場景

In some cases, you want to pass data through the component tree without having to pass the props down manually at every level.

context 的本質在于為元件樹提供一種跨層級通信的能力,原本在 React 隻能通過 props 逐層傳遞資料,而 context 打破了這一層束縛。

context 雖然不被建議使用,但在一些流行庫中卻非常常見,例如:react-redux、react-router。究其原因,我認為是單一頂層與多樣底層間不是單純父子關系的結果。例如:react-redux 中的 Provider,react-router 中的 Router,均在頂層控制 store 資訊與路由資訊。而對于 Connect 與 Route 而言,它們在 view 中的層級是多樣化的,通過 context 擷取頂層 Provider 與 Router 中的相關資訊再合适不過。

context 的坑

  • context 相當于一個全局變量,難以追溯資料源,很難找到是在哪個地方中對 context 進行了更新。
  • 元件中依賴 context,會使元件耦合度提高,既不利于元件複用,也不利于元件測試。
  • 當 props 改變或是 setState 被調用,getChildContext 也會被調用,生成新的 context,但 shouldComponentUpdate 傳回的 false 會 block 住 context,導緻沒有更新,這也是精讀文章的重點内容。

4 總結

正如精讀文章開頭所說,context 是一個非常強大的,具有很多免責聲明的特性,就像伊甸園中的禁果。的确,引入全局變量似乎是應用混亂的開始,而 context 與 props/state 相比也實屬異類。在業務代碼中,我們應抵制使用 context,而在架構和庫中可結合場景适當使用,相信 context 也并非洪水猛獸。

讨論位址是:精讀《How to safely use React context》· Issue #23 · dt-fe/weekly