Design Pattern In React — Compound component (複合元件)

--

相信大家對 design pattern (設計模式) 這個詞並不陌生,並在聽到的當下可能腦中會浮現一些較為人所知的 pattern,例如 Singleton、Factory、Proxy…等。而程式開發中所謂的 pattern ,則代表著 “經過開發者反覆的使用與測試,被大多數人知道與認同最終被歸類的程式開發經驗總結。” pattern 所帶來的好處有使測試更容易、程式碼更簡潔與易維護、理解該 pattern 的共同開發者可以快速接手專案…等。然而什麼時機下需要使用何種 design pattern 卻是需要經過慎重衡量的,要是在錯誤的狀況下使用錯誤的 pattern,對於開發與維護可能是帶來反效果的。

Design Pattern In React

隨著近幾年 web app 漸趨複雜,前端的程式碼也隨之變得越來越複雜與龐大,因此也更需要注重程式碼的可維護性與可擴展性。React 的開發是建立在 component 這個基礎上的,整個頁面是由大大小小的 component 所組成,因此社群中發展出了一些著重在 component 上的 design pattern,希望能提升 component 在開發上與使用上的易用性與維護性。

Compound Component 複合元件

所謂的 compound component 可以理解為兩個或兩個以上的 component 結合成一個擁有特定功能或是能夠達成特定任務的 component,通常會有一個 component 為 parent,其他則為 children,而當其中一個 component 單獨存在時,是沒有意義的。以下舉一個純 HTML 元素的例子:

<select>  <option value="value1">key1</option>  <option value="value2">key2</option></select>

這是大家應該都很熟悉的選擇元件,如果只有 select 而沒有 option,或是只有 option 存在,外面沒有被 select 包覆,這樣的語法都是沒有意義的,這樣應該可以清楚理解上面提到的 compound component 的特性了吧。

參考 Kent C 的文章 ,如果要做一個可以 toggle 的 button,也就是可以依據 state 是 on 或是 off 來顯示不同的文字或樣式,你會怎麼做?

你可能會想到利用 props 傳入不同的文字來決定在不同狀態時要顯示什麼。

然而這樣子的做法卻有幾個明顯的缺點:

  • 顯示順序不易控制
  • props 越來越多時易造成混亂

這時透過 compound component 則可以輕鬆解決這些問題。compound component 主要是將要渲染的 UI 以 props.children 的方式傳入父層元件,父層元件提供 state 供子元件做使用,對於使用者來說,他只需要傳入想要的 子元件,不用知道父層與子層之間如何溝通,顯示的順序更可以輕易按自己意志調換,父子元件達成隱含狀態的共享(implicit state)。

在過去 class component 主宰的時代,要實現 compound component 大多靠 React.cloneElement 這個方式,但現今 react 已經進入 hook 時代,我們就應該學習用較新的方式去達成ㄧ樣的設計模式,於是主角登場啦 — Context API!

Parent component 的工作即是 context provider,並把子元件會用到的 state 帶進 provider 中, 最後 return 包住 props.children 的 Context Provider 就 ok 了,如此一來子元件的內容是完全依照使用者的需求去塞入的,當需要特定的 state 來做 UI 的邏輯判斷時再去跟 context 拿來用就好囉,這樣的 API 設計明顯比第一種方法好上許多吧!

程式碼實作

我參考 Kent C 的文章做了點修改,並且使用 TypeScript 實作一個簡單的透過按鈕可以切換兩種背景顏色的範例:

這邊也就是一些基本的 React 應用就不多去解釋,值得一提的是這兩行

ToggleElement.Container = Container;ToggleElement.Btn = Btn;

這樣的寫法是不強制的,但我個人偏好將 child component 存成 parent component 的 static property,會提高整體的易讀性。

使用上就會長這個樣子

剛剛也提及了使用 compound component 是有可以自行決定 children UI 顯示順序或次數這個優勢的,如果你嫌一個 btn 不夠,你可以改成

四個擁有一樣功能的按鈕就出現了呢!

(當然這是最簡單的示例,你可以再去對元件做任何想要的擴展。)

結語

本篇介紹了 Compound Component 的概念,並透過一個簡單與粗陋(廢話,看 code 跟成品就知道有多粗陋了)的範例解釋 Compound Component 的實作方式與優勢。然而還是要謹記文章開頭提及的:“設計模式需要在適當的情況下才能發揮作用”,因此在選擇應用某個 design pattern 前一定要謹慎評估當前的狀況適不適合使用該設計模式。

--

--

莫力全 Kyle Mo

什麼都想學的雜食性軟體工程師 🇹🇼 (https://github.com/kylemocode) 合作與聯繫 📪 oldmo860617@gmail.com IG 技術自媒體:@kylemo.webdev.life