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 memanggiluseContext(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>
}
createContext("light")membuat context. Panggil sekali, di luar komponen."light"adalah nilai bawaannya.<ThemeContext.Provider value="dark">menyediakan nilai"dark"untuk semua komponen di dalamnya, sedalam apa pun. Di React 19 boleh juga ditulis<ThemeContext value="dark">;.Providerjalan di semua versi dan dipakai di latihan.const theme = useContext(ThemeContext)membaca nilai dari Provider terdekat di atasnya, lalu{theme}menampilkannya.LayoutdanToolbarsekarang gak menerima props apa pun.
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 |
- Saat
valueProvider berubah, semua komponen yang memanggiluseContext(ThemeContext)render ulang dengan nilai baru. Gak bisa dilewati. LayoutdanToolbartetap 1 karena<Layout />dibuat olehApp, danAppgak render ulang; yang menyimpan state cumaThemeProvider.- Kalau state dan Provider ditulis langsung di
Appbersama<Layout />,Layoutikut render ulang seperti biasa, karena induknya render ulang. Context menghapus props yang dioper, bukan otomatis menghemat render.
🎯 Context atau props?
- Data global yang jarang berubah (tema, bahasa, user yang login): Context.
- Data untuk anak langsung atau dua lapis: props biasa, lebih jelas asalnya.
- State yang cuma dipakai satu komponen (dropdown terbuka atau tertutup):
useStatedi komponen itu.
⚠️ Jebakan yang sering ketemu
- Lupa Provider. Gak ada error, cuma nilai bawaan yang muncul. Cek posisi komponennya: harus di dalam Provider.
- Mengetik nilainya langsung.
<p id="theme">dark</p>tampil benar, tapi gak membaca context. Simpan dulu ke variabel,const theme = useContext(ThemeContext), lalu tampilkan{theme}. - Destructuring nilai yang bukan object. Nilainya string
"dark", jadiconst { theme } = useContext(ThemeContext)menghasilkanundefined. Destructuring cuma untuk value berupa object. - Provider tanpa
value.<ThemeContext.Provider>tanpavaluemenyediakanundefined, bukan nilai bawaan.
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.