Mục lục

Compound Component: Xây dựng UI Library nâng cao

Học cách thiết kế API component 'Flexible' như Radix UI. Chia nhỏ component (Sub-components), giao tiếp ngầm bằng Context. Case study: Xây dựng Toggle List.

Bạn muốn cái <Select> của mình linh hoạt? Bạn muốn user có thể đổi chỗ cái Icon mũi tên sang trái hay phải tùy thích? Đừng dùng props (iconPosition="left"). Hãy dùng Compound Component.

1. Vấn đề: "Config Explosion"

tsx:
// ❌ Props phình to không kiểm soát
<Select
  options={...}
  icon="chevron"
  showSearch
  searchPlaceholder="..."
  renderOption={(opt) => ...}
  dropdownClassName="..." 
/>

Một component gánh quá nhiều trách nhiệm.

2. Giải pháp: Chia để trị (Sub-Components)

tsx:
// ✅ Linh hoạt tuyệt đối
<Select>
  <Select.Trigger>
    <Select.Value />
    <Select.Icon />
  </Select.Trigger>
  
  <Select.Content>
    <Select.SearchInput />
    <Select.Item value="1">Option 1</Select.Item>
    <Select.Item value="2">Option 2</Select.Item>
  </Select.Content>
</Select>

3. Implementation (Dùng Context)

Các component con (Item, Trigger) cần state chung (isOpen, selectedValue). Ta dùng Context để nối chúng lại.

tsx:
import { createContext, useContext, useState } from "react";

// 1. Tạo Context
const ToggleContext = createContext();

// 2. Component Cha (Provider)
function ToggleList({ children }) {
  const [on, setOn] = useState(false);
  const toggle = () => setOn(!on);
  
  return (
    <ToggleContext.Provider value={{ on, toggle }}>
      {children}
    </ToggleContext.Provider>
  );
}

// 3. Component Con (Consumer)
ToggleList.On = ({ children }) => {
  const { on } = useContext(ToggleContext);
  return on ? children : null;
};

ToggleList.Off = ({ children }) => {
  const { on } = useContext(ToggleContext);
  return on ? null : children;
};

ToggleList.Button = ({ children }) => {
  const { toggle } = useContext(ToggleContext);
  return <button onClick={toggle}>{children}</button>;
};

4. Usage

tsx:
<ToggleList>
  <ToggleList.Button>Click me</ToggleList.Button>
  <ToggleList.On>The light is ON! 💡</ToggleList.On>
  <ToggleList.Off>The light is OFF! 🌑</ToggleList.Off>
</ToggleList>

Kết luận

Compound Component cho phép người dùng component (Consumers) có toàn quyền quyết định về Cấu trúc (Layout)Thứ tự (Ordering), trong khi component cha nắm giữ Logic (State). Đây là đỉnh cao của Reusable Component Design.

Quảng cáo
mdhorizontal