Halo, Sobat Ngoding! 👋
Pernah nggak sih kamu coba bikin komponen yang bisa dipake ulang (reusable), kayak Tabs atau Accordion, tapi ujung-ujungnya malah ribet sendiri karena harus ngoper banyak banget props?
Jadinya malah kayak gini nih, serem:
// Pendekatan "God Component"
<Tabs
data={[
{ label: "Beranda", content: "Selamat datang!" },
{ label: "Profil", content: "Profil user di sini." }
]}
theme="dark"
activeTabIndex={0}
onTabChange={handleTabChange}
tabClassName="text-bold"
panelClassName="p-4"
/>
Jalan sih jalan, tapi kaku banget. Gimana kalau kamu mau nambahin ikon di satu tab aja? Atau mau disable tab kedua? Kamu harus ngubah struktur datanya lagi. Ribet kan? 🤯
Kenalin nih, Compound Component Pattern.
Apaan tuh?
Inget tag HTML standar kayak <select> dan <option>? Mereka nggak kerja sendiri-sendiri, tapi kerjasama.
<select>
<option value="1">Pilihan 1</option>
<option value="2">Pilihan 2</option>
</select>
Si <select> yang ngatur state-nya, terus si <option> yang nampilin pilihannya. Mereka berbagi state secara implisit (diam-diam). Kita bisa lho bikin yang kayak gini di React!
Langkah 1: Bikin Context-nya
Pertama, kita butuh cara biar si induk (Tabs) bisa ngobrol sama anak-anaknya (Tab dan TabPanel) tanpa harus ngoper props satu-satu. Kita pake React Context buat ini.
import React, { createContext, useContext, useState } from 'react';
// 1. Bikin Context
const TabsContext = createContext();
// 2. Bikin Komponen Induk (Parent)
const Tabs = ({ children, defaultIndex = 0 }) => {
const [activeIndex, setActiveIndex] = useState(defaultIndex);
return (
<TabsContext.Provider value={{ activeIndex, setActiveIndex }}>
<div className="tabs-container">
{children}
</div>
</TabsContext.Provider>
);
};
Di sini, Tabs megang state (activeIndex) dan nyediain itu buat siapa aja yang ada di dalemnya.
Langkah 2: Bikin Sub-Komponennya
Sekarang, ayo kita bikin komponen-komponen yang bakal make context ini.
// 3. Bikin List Tab (pembungkus opsional buat styling)
const TabList = ({ children }) => {
return <div className="flex border-b">{children}</div>;
};
// 4. Bikin Tab individunya
const Tab = ({ index, children }) => {
const { activeIndex, setActiveIndex } = useContext(TabsContext);
const isActive = activeIndex === index;
return (
<button
onClick={() => setActiveIndex(index)}
className={`p-2 ${isActive ? 'border-b-2 border-blue-500 font-bold' : 'text-gray-500'}`}
>
{children}
</button>
);
};
// 5. Bikin Panel Kontennya
const TabPanel = ({ index, children }) => {
const { activeIndex } = useContext(TabsContext);
if (activeIndex !== index) return null;
return <div className="p-4">{children}</div>;
};
Langkah 3: Pake Deh!
Coba liat deh, API komponen kita jadi bersih dan fleksibel banget:
const App = () => {
return (
<Tabs defaultIndex={0}>
<TabList>
<Tab index={0}>🏠 Beranda</Tab>
<Tab index={1}>👤 Profil</Tab>
<Tab index={2}>⚙️ Pengaturan</Tab>
</TabList>
<TabPanel index={0}>
<h2>Selamat Datang di Dashboard</h2>
<p>Ini pake teknik Compound Component lho!</p>
</TabPanel>
<TabPanel index={1}>
<UserProfile />
</TabPanel>
<TabPanel index={2}>
<p>Pengaturan ada di sini...</p>
</TabPanel>
</Tabs>
);
};
Kenapa ini lebih oke?
- Fleksibilitas: Saya bisa gampang nuker urutan tab, ngebungkus pake div lain buat styling, atau nambah ikon tanpa ngubah logika
Tabs-nya. - Keterbacaan: Kodenya keliatan kayak HTML biasa. Strukturnya langsung jelas.
- Pemisahan Tugas:
Tabsngurusin logika (state), sementaraTabdanTabPanelngurusin tampilan.
Kapan harus pake?
Pake pola ini kalau kamu punya sekumpulan komponen yang perlu berbagi state dan logika secara implisit, contohnya:
- Accordions (Item, Header, Content)
- Tabs (List, Tab, Panel)
- Dropdown Menus (Button, Menu, Item)
- Form Groups (Label, Input, ErrorMessage)
Penutup
Compound Component pattern ini alat yang ampuh banget di React. Ini mindahin kerumitan dari si pemakai (kamu saat ini) ke si pembuat library (kamu juga, tapi versi masa depan).
Mulai deh pake ini buat komponen UI kamu, dijamin diri kamu di masa depan bakal berterima kasih!
Selamat Ngoding! 🚀