我找到了一個當我們在寫react
應用時非常有用的模式。如果你寫過一段時間的react,你可能已經發現了它。這篇文站很好的解釋了它,但是我想補充一些觀點。
如果將元件劃分為兩類,你會發現元件重用起來更加容易。我把這兩類稱為Container
和Presentational
。也有其他叫法,Fat
和 Skinny
, Smart
和 Dumb
, Stateful
和 Pure
, Screens
和 Components
等等。這些都不完全相同,但是核心思想是相似的。
我的presentational
元件:
- 關心事物如何展示;
- 也許會同時包含展示類元件和容器元件,並且通常有一些DOM操作和自己的樣式;
- 通常允許
this.props.chidren
放在容器裡; - 對應用的其餘部分沒有依賴,比如
Flux actions
或者stores
; - 不會說明資料是如何載入和變化的;
- 只通過
props
接受資料和回撥函式; - 幾乎沒有自己的
state
(即使有,也是UI
相關的,而不是data
相關的); - 通常被寫作函式式元件除非需要狀態、生命週期的鉤子、效能優化;
- 例子:
Page
,Sidebar
,Story
,UserInfo
,List
。
我的container
元件:
- 關心事物如何運作;
- 也許會同時包含展示類元件和容器元件,但是除了一些用來包含元素的div通常不會其他有DOM操作,並且不會包含任何樣式;
- 為
presentational
元件或其他container
元件提供資料和行為; - 通常是有狀態的,因為它們往往作為資料來源;
- 通常使用像
React Redux
的connect()
、Relay
的createContainer()
、Flux Utils
的Container.create()
這樣的高階元件來生成,而不是手動編寫; - 例子:
UserPage
,FollowersSidebar
,StoryContainer
,FollowedUserList
。
這種方式的好處:
- 更好的關注分離。通過這種方式編寫元件,你能更好的理解你的應用和試圖;
- 更好的可重用性。你可以將相同的
presentational
元件和完全不同的狀態組合在一起,並將這些轉化為可進一步重用的獨立的容器元件; presentational
元件本質上是你應用的“調色盤”。你可以將它們放在一個單頁面,設計師不接觸應用的邏輯就可以調整所有的變化;- 這迫使你提取你的佈局元件比如
Sidebar
,Page
,ContextMenu
,使用this.props.children
而不是在幾個容器元件之間複製標籤和佈局。
什麼時候引入Containers?
我建議你一開始只用presentational
元件來構建應用。最後你會意識到通過中間元件傳遞了太多的props
。當你注意到一些元件並不使用它們收到的props
而幾乎只是向下傳遞,並且當子元件需要更多資料的時候你得重寫所有的中間元件,這就是應該引入container
元件的好時候了。通過這種方式,你可以通過葉元件得到需要的資料和行為,而不會加重元件樹中不相關的元件。
這是一個持續重構的過程,所以不要嘗試一次就做的好。當你嘗試這種模式,你會形成一種什麼時候該提取一個container的直覺,就像你知道什麼時候該提取一個函式一樣。我的free Redux Egghead series也能在這方面幫助你。
其他分類
重要的是要理解presentational
元件和container
元件之間的區別不是一個技術問題,相反,區別在於它們的目的。
相比之下,這裡有一些相關(但不同!)技術的區別:
- 有狀態的和無狀態的。一些元件使用
React
的setState()
方法但有些不使用,當presentational
元件往往是無狀態的,container
元件往往是有狀態,這不是一個硬性的規則。presentational
元件可以是有狀態的,container
元件也可以是無狀態的。 - 類和函式。從
React0.14
開始,元件可以以函式或類的方式宣告。函式式元件定義更簡單,但是缺乏一些類特有的功能。這些限制也許在未來會解決但是如今確實存在。由於函式式元件更容易理解,所以我建議你使用函式式元件,除非你需要狀態、生命週期鉤子和效能優化這些目前只提供給類的功能。 - 純和不純。如果給一個元件輸入相同的
state
和props
,保證會輸出相同的結果,大家稱這樣的元件為純元件。純元件可以使用函式或類來宣告,並且可以是有狀態的或無狀態的。純元件另一個重要的方面是它們不依賴props
和state
的深度變化,所以它們的渲染效能可以在shouldComponentUpdate()鉤子函式裡通過淺比較進行優化。目前只支援在類中定義shouldComponentUpdate()
,但是未來也許會改變。
presentational
元件和container
元件都可以是任意一種狀態。以我的經驗,presentational
元件往往是無狀態的純函式(pure functions),container
元件往往是有狀態的純類(pure classes),然而這只是觀察得來的結論而不是一種規則,我曾經也看到過完全相反但是合理的情況。
不要把分離presentational
元件和container
元件當做一種教條。有時候並沒那麼重要或很難劃清界限,如果你不確定一個元件該是presentational
元件或container
元件,也許是決定的太早了。別擔心!