Nếu bạn thấy mình đang truyền prop user qua 5 tầng component (App -> Layout -> Header -> UserMenu -> Avatar), bạn đang gặp vấn đề Prop Drilling.
Junior Dev dùng Context để fix. Senior Dev dùng Composition.
1. Vấn đề: Component "Biết Tuốt"
// ❌ Component cha phải biết quá nhiều thứ về con
function Header({ user, onLogout, toggleTheme, searchResult }) {
return (
<div className="header">
<Logo />
<SearchBox results={searchResult} />
<UserMenu user={user} onLogout={onLogout} toggleTheme={toggleTheme} />
</div>
);
}Vấn đề: Mỗi khi UserMenu cần thêm props mới, Header cũng phải sửa code để truyền hộ. Header trở thành "Trung chuyển Props".
2. Giải pháp: Composition (children)
Hãy để component cha "đục lỗ" và component ông nội tự nhét con vào.
// ✅ Header chỉ lo layout, không lo data của con
function Header({ children }) {
return <div className="header">{children}</div>;
}
// Usage
function App() {
return (
<Header>
<Logo />
<SearchBox />
<UserMenu /> {/* App truyền props trực tiếp cho UserMenu nếu cần */}
</Header>
);
}Đây chính là Inversion of Control. Header không còn quản lý UserMenu nữa.
3. Advanced: The "Slots" Pattern
React children chỉ có 1 lỗ. Nếu Layout của bạn phức tạp (Header, Sidebar, Footer, Main)?
Hãy dùng props như là các "Slots" (Giống Vue/Angular).
function DashboardLayout({ leftSidebar, content, topBar }) {
return (
<div className="grid">
<aside>{leftSidebar}</aside>
<div className="main">
<header>{topBar}</header>
<main>{content}</main>
</div>
</div>
);
}
// Usage
<DashboardLayout
leftSidebar={<Sidebar />}
topBar={<NavBar user={user} />}
content={<PageContent />}
/>Lợi ích:
- No Prop Drilling: Data đi thẳng từ nơi gọi (
App) vào nơi dùng (Sidebar), không qua trung gian. - Performance: Khi
userđổi, chỉNavBarre-render.DashboardLayoutvàSidebarkhông bị re-render (vì chúng chỉ là props được truyền vào).
Kết luận
Trước khi dùng Context, hãy thử Composition. Context làm các component dính chặt vào nhau (Coupling). Composition giúp chúng tách rời và dễ tái sử dụng.