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…

Context adalah cara mengirim sebuah nilai dari komponen di atas ke komponen mana pun di bawahnya, tanpa mengopernya lewat props di tiap lapis. Mirip speaker pengumuman di sekolah. Daripada kepala sekolah menitip pesan ke wali kelas, wali kelas ke ketua kelas, ketua kelas ke tiap murid, dia bicara sekali lewat speaker, dan siapa pun yang mendengarkan langsung dapat pesannya. Kalau pengumumannya berganti, semua pendengar dapat versi baru.

🧪 Tebakan cepat: kamu menulis createContext("light"), tapi lupa memasang Provider. Komponen yang memanggil useContext(ThemeContext) dapat apa: "light", undefined, atau error? Tebak dulu, jawabannya ada di bagian "Lupa Provider: nilai bawaan".

Masalahnya: prop drilling

Komponen ThemedButton dan Footer butuh theme. Nilainya disimpan di App, dua lapis di atasnya:

function App() {
  const [theme, setTheme] = useState("dark")
  return <Layout theme={theme} />
}

function Layout({ theme }) {
  return (
    <div>
      <Toolbar theme={theme} />
      <Footer theme={theme} />
    </div>
  )
}

function Toolbar({ theme }) {
  return (
    <nav>
      <ThemedButton theme={theme} />
    </nav>
  )
}

Layout dan Toolbar gak memakai theme sama sekali. Mereka menerimanya cuma untuk diteruskan ke bawah. Mengoper props lewat komponen yang gak butuh seperti ini disebut prop drilling. Dua lapis masih wajar; sepuluh lapis berarti sepuluh komponen yang harus diubah tiap kali ada data baru.

Tiga langkah Context

import { createContext, useContext } from "react"

// 1. Bikin context, dengan nilai bawaan "light"
const ThemeContext = createContext("light")

function App() {
  // 2. Provider menyediakan nilai untuk semua komponen di dalamnya
  return (
    <ThemeContext.Provider value="dark">
      <Layout />
    </ThemeContext.Provider>
  )
}

function Layout() {
  return (
    <div>
      <Toolbar />
      <Footer />
    </div>
  )
}

function Toolbar() {
  return (
    <nav>
      <ThemedButton />
    </nav>
  )
}

function ThemedButton() {
  // 3. Komponen mana pun di dalam Provider bisa membacanya
  const theme = useContext(ThemeContext)
  return <button className={theme}>Tombol {theme}</button>
}

function Footer() {
  const theme = useContext(ThemeContext)
  return <footer>Tema: {theme}</footer>
}

Hasilnya: tombol bertuliskan Tombol dark dan footer Tema: dark.

Lupa Provider: nilai bawaan

Di atas komponennya useContext(ThemeContext) memberi
<ThemeContext.Provider value="dark"> "dark"
Gak ada Provider sama sekali "light", nilai bawaan dari createContext

Tanpa Provider, gak ada error. useContext diam-diam memberi nilai bawaan, "light". Itu jawaban tebakannya. Makanya pilih nilai bawaan yang masuk akal, dan kalau aplikasimu "kok temanya gak berubah", cek dulu apakah komponennya benar-benar ada di dalam Provider.

Nilainya berubah: siapa yang render ulang

Biasanya nilai Provider berasal dari state, dan Provider-nya dibungkus komponen sendiri:

function ThemeProvider({ children }) {
  const [theme, setTheme] = useState("dark")
  const toggle = () => setTheme(theme === "dark" ? "light" : "dark")

  return (
    <ThemeContext.Provider value={theme}>
      <button onClick={toggle}>Ganti tema</button>
      {children}
    </ThemeContext.Provider>
  )
}

function App() {
  return (
    <ThemeProvider>
      <Layout />
    </ThemeProvider>
  )
}

Klik Ganti tema sekali, lalu hitung berapa kali tiap komponen sudah render (render pertama ikut dihitung):

Komponen Prop drilling Context
App 2 1
ThemeProvider (gak ada) 2
Layout 2 1
Toolbar 2 1
ThemedButton 2 2
Footer 2 2

🎯 Context atau props?

⚠️ Jebakan yang sering ketemu

TL;DR: Context mengatasi prop drilling, yaitu mengoper props lewat komponen yang gak butuh. Tiga langkah: const ThemeContext = createContext("light") di luar komponen, bungkus bagian pohon dengan <ThemeContext.Provider value="dark">, lalu baca di mana saja di bawahnya dengan const theme = useContext(ThemeContext) dan tampilkan {theme}. Tanpa Provider, useContext memberi nilai bawaan tanpa error. Saat value berubah, semua komponen yang memanggil useContext render ulang. Pakai Context untuk data global yang jarang berubah; untuk satu dua lapis, props biasa sudah cukup.

Yang akan kamu pelajari