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) và Thứ tự (Ordering), trong khi component cha nắm giữ Logic (State). Đây là đỉnh cao của Reusable Component Design.