上篇文章中介紹了如何使用 Container/Presentational Pattern 來實作通用的 <Table>
元件,並且把資料管理和選擇邏輯分離到容器元件中。今天則要來討論 Compound Pattern~
我自己的理解是,Compound Pattern 適合用於父元件內包含多個子元件的情境,且這些子元件需要根據不同狀態或邏輯動態顯示或隱藏時。使用 Compound Pattern 可以很自由地挑選出要出現的子元件,而且能保持清晰的結構與邏輯。
先提一個比較好理解的例子:
以下是一個簡單的卡片元件 <Card>
:
// 元件的架構被固定住,無法靈活地調整卡片的內容或結構
export default function Card(props) {
return (
<div>
<h2>{props.title}</h2>
<p>{props.content}</p>
<button>Read More</button>
</div>
);
}
這樣的寫法雖然簡單,但問題在於當我們希望卡片元件可以在不同情境下顯示不同內容(例如不同的標題、描述、按鈕)時,我們必須在 Card
元件內進行修改或增加大量的 props,這樣會讓元件變得難以維護。
為了讓這個卡片元件能夠更靈活地控制其內部的結構,我們可以使用 Compound Pattern。這樣做可以讓父元件 <Card>
提供一個上下文(Context),允許我們在子元件中選擇性地使用不同的內容組合。
使用 Compound Pattern 改寫的範例:
CardContext
以提供共享的狀態:import { createContext } from 'react';
const CardContext = createContext();
const Card = ({ children }) => {
return <CardContext.Provider value={{}}>{children}</CardContext.Provider>;
};
const CardTitle = ({ children }) => <h2>{children}</h2>;
const CardContent = ({ children }) => <p>{children}</p>;
const CardButton = ({ children }) => <button>{children}</button>;
<Card>
中,自由地組合這些子元件來構建卡片的結構:const App = () => {
return (
<Card>
<CardTitle>自訂的標題</CardTitle>
<CardContent>這是卡片的內容。</CardContent>
<CardButton>了解更多</CardButton>
</Card>
);
};
export default App;
這樣,我們就可以根據需要隨意組合卡片的內容,而不需要更改父元件或子元件的程式碼。這就是 Compound Pattern 的強大之處!
在 Compound Pattern 的文章中,有特別提到這樣的設計模式很適合用在 select、dropdown、menu items 上,舉例來說可能是:
為什麼使用 Context
來取得狀態?
在 React 中,父元件與子元件間的資料傳遞通常是透過 props
完成的,這很適合單純的元件結構。然而,當一個應用程式的層級變得較為複雜時,例如某個深層的子元件需要使用父元件的某個狀態,就需要一層一層地傳遞 props
,這就是所謂的 prop drilling
問題。
Context
提供了一種解決方案,它允許我們將狀態存在一個「上下文」中,這樣任何需要使用這個狀態的子元件都可以直接透過 useContext
來取得,而無需經過每一層元件傳遞 props
。