Bayangin app dengan struktur dalam: <App> → <Layout> → <Sidebar> → <UserMenu> → <Avatar>. Komponen <Avatar> butuh data user (foto, nama). Cara naive: pass user prop dari App → drilled lewat 4 level komponen perantara, padahal mereka gak butuh data itu — cuma jadi pipa transit. Ini namanya prop drilling — anti-pattern yang bikin refactor susah, props parameter bloat. Context API ngasih cara: nge-broadcast data dari satu titik (Provider) ke semua descendant, gak peduli sedalam apa, tanpa drilling.
💡 Filosofi: Context buat data yang BENER-BENER global. Theme, current user, locale/language, app config. JANGAN over-use Context buat data lokal yang cuma butuh 2-3 level — itu prop drilling level wajar, masih clean. Pattern penting: jangan jadi state management replacement (pakai Zustand/Redux untuk itu).
Membuat Context
import { createContext, useContext, useState } from "react"
// 1. Bikin context (default value untuk fallback)
const ThemeContext = createContext("light")
function App() {
const [theme, setTheme] = useState("dark")
// 2. Wrap children dengan Provider, kasih value
return (
<ThemeContext.Provider value={theme}>
<Toolbar />
</ThemeContext.Provider>
)
}
function Toolbar() {
return <ThemedButton />
}
function ThemedButton() {
// 3. Di komponen mana pun (dalam Provider), akses value
const theme = useContext(ThemeContext)
return <button className={theme}>Tombol {theme}</button>
}
<ThemedButton> 3 level dalam dari Provider — tetap akses theme tanpa props.
Pattern Standar — Context + Custom Hook
const AuthContext = createContext(null)
export function AuthProvider({ children }) {
const [user, setUser] = useState(null)
const login = (credentials) => { /* ... */ }
const logout = () => { /* ... */ }
return (
<AuthContext.Provider value={{ user, login, logout }}>
{children}
</AuthContext.Provider>
)
}
// Custom hook biar API rapi
export function useAuth() {
const ctx = useContext(AuthContext)
if (!ctx) throw new Error("useAuth must be used inside AuthProvider")
return ctx
}
// Pemakaian — clean
function ProfilePage() {
const { user, logout } = useAuth()
return <button onClick={logout}>Halo {user.name}</button>
}
🎭 Analogi sehari-hari
Context API itu kayak siaran radio kantor. Daripada manajemen kasih pengumuman ke kepala divisi, divisi bilang ke kepala tim, tim bilang ke karyawan satu-satu (prop drilling) — pasang speaker pengumuman (Provider) di atap, semua yang di dalam gedung dengar langsung pesan terbaru (useContext). Yang gak butuh dengar pengumuman gak terganggu (komponen yang gak useContext-nya). Pas isi pengumuman berubah (state Provider update), semua pendengar otomatis re-render dengan info baru. Tapi awas: jangan tiap pesan pribadi disiarkan via speaker kantor — buat data lokal pakai props biasa atau lift state up.
⚠️ Jebakan yang sering ditemui
- Re-render kebanyakan — Context value berubah → SEMUA consumer re-render. Object literal
value={{user, login}}bikin reference baru tiap render → re-render mass. PakaiuseMemodi value atau split context. - Lupa Provider —
useContext(MyContext)di luar Provider = return default value (bisa null). Tambahin error throw di custom hook (lihat AuthProvider pattern). - Initial value misleading —
createContext("light")initial cuma dipake kalau gak ada Provider di atas. Sering jadi "ngira context jalan" tapi sebenernya pakai default. - Pakai Context buat semua state global — Context cocok untuk SEDIKIT data global (theme, user, locale). Buat banyak state app dengan complex updates → pakai Zustand/Redux/Jotai.
- State + setter di satu Context — pas state berubah, komponen yang cuma butuh setter ikut re-render. Split jadi 2 context:
UserContext+UserDispatchContext. - Forward Context cross-package — gak jalan di Next.js Server Components atau micro-frontend. Wrap di top of bundle.
🎯 Pakai Context atau alternative — pilih mana?
- 2-3 level prop drilling → BIARIN, props masih OK
- Theme, user auth, locale/language → Context API ✓
- App config, feature flags → Context API ✓
- State management kompleks (cart, filters, dashboard data) → Zustand atau Redux
- Server data + cache → TanStack Query (built-in cache, gak perlu Context)
- URL state (search params, page) → URL params (router)
- Form state → React Hook Form (Form lib udah handle)
Aturan: Context = "data global yang JARANG berubah". State management lib = "data global yang SERING berubah dengan banyak update pattern".
TL;DR: Context API = solve prop drilling. createContext() + <Context.Provider value={...}> di parent + useContext(MyContext) di descendant. Wrap dengan custom hook (useAuth, useTheme) untuk API clean. Cocok buat theme/user/locale (data global jarang berubah). Awas re-render mass — pakai useMemo di value atau split context. JANGAN replace state management library — Context bukan Zustand/Redux.