Context API — React

Bayangin app dengan struktur dalam: → → → → . Komponen butuh data user (foto, nama). Cara naive: pass user prop dari App → drilled lewat 4 level komponen perant

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

🎯 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.

Yang akan kamu pelajari