Context membagikan satu nilai ke semua komponen di bawah sebuah Provider, tanpa mengopernya lewat props dari level ke level. createContext(nilaiBawaan) membuat wadahnya, <X.Provider value={...}> menyediakan nilainya untuk semua yang ada di dalamnya, dan useContext(X) membacanya dari Provider terdekat di atas. Bayangin Toko Buku Kak Nadia punya cabang di Kuala Lumpur. Label harga di rak gak diberi tahu mata uangnya satu per satu. Tiap label membaca papan di dinding toko tempat dia digantung: di toko Jakarta tertulis Rp, di cabang Kuala Lumpur tertulis RM. Label yang dicetak di brosur, di luar toko mana pun, memakai mata uang yang tercetak di labelnya sendiri, yaitu nilai bawaannya.
🧪 Tebakan cepat:
Headerditaruh di luar<TokoContext.Provider>, dan kartu-kartu buku di dalamnya. Provider-nya membawa signaltema. Kamu klikGanti temadan semua kartu jadi gelap.Headerikut gelap atau gak? Jawabannya ada di bagian "Di luar Provider: nilai bawaan".
createContext, Provider, useContext
import { createContext, useContext } from "solid-js"
const ThemeContext = createContext("light")
function ThemedText() {
const theme = useContext(ThemeContext)
return <p>Tema: {theme}</p>
}
function App() {
return (
<ThemeContext.Provider value="dark">
<ThemedText />
</ThemeContext.Provider>
)
}
Hasilnya Tema: dark.
createContext("light")membuat context dengan nilai bawaan"light". Dibuat sekali, di luar komponen, supaya penyedia dan pembacanya memakai context yang sama.<ThemeContext.Provider value="dark">menyediakan"dark"untuk semua komponen di dalamnya, sedalam apa pun.useContext(ThemeContext)diThemedTextmencari Provider terdekat di atasnya dan mengembalikanvalue-nya.ThemedTextgak menerima prop apa pun. Ini latihan pertama.
Di luar Provider: nilai bawaan
Toko Buku Kak Nadia dengan mata uang dan tema di context:
import { createContext, createSignal, useContext } from "solid-js"
const TokoContext = createContext({ mataUang: "Rp", tema: () => "terang" })
function Header() {
const toko = useContext(TokoContext)
return <header class={toko.tema()}>Toko Buku Kak Nadia · harga dalam {toko.mataUang}</header>
}
function KartuBuku(props) {
const toko = useContext(TokoContext)
return (
<div class={"kartu " + toko.tema()}>
<h3>{props.judul}</h3>
<p>{toko.mataUang}{props.harga}</p>
</div>
)
}
function App() {
const [tema, setTema] = createSignal("terang")
const ganti = () => setTema(t => (t === "terang" ? "gelap" : "terang"))
return (
<div>
<Header />
<TokoContext.Provider value={{ mataUang: "Rp", tema }}>
<button onClick={ganti}>Ganti tema</button>
<KartuBuku judul="Laskar Pelangi" harga={89000} />
<KartuBuku judul="Bumi Manusia" harga={120000} />
<TokoContext.Provider value={{ mataUang: "RM", tema }}>
<KartuBuku judul="Laskar Pelangi (cabang KL)" harga={25} />
</TokoContext.Provider>
</TokoContext.Provider>
</div>
)
}
Diuji di Solid 1.9, sesudah Ganti tema diklik:
| Komponen | useContext dapat dari |
Tampil |
|---|---|---|
Header |
gak ada Provider di atasnya: nilai bawaan | harga dalam Rp, kelas terang |
| Kartu Laskar Pelangi, Bumi Manusia | Provider luar | Rp89000, Rp120000, kelas kartu gelap |
| Kartu cabang KL | Provider dalam, yang terdekat | RM25, kelas kartu gelap |
Header tetap terang. Itu jawaban tebakannya. Dia gak berada di dalam Provider mana pun, jadi useContext mengembalikan nilai bawaan dari createContext, yang temanya selalu "terang". Context cuma sampai ke komponen di dalam Provider-nya. Kalau context dibuat tanpa nilai bawaan, createContext(), komponen di luar Provider mendapat undefined, dan toko.mataUang berhenti dengan TypeError: Cannot read properties of undefined (reading 'mataUang'). Kartu cabang KL ada di dalam dua Provider, dan yang terdekat menang: RM25.
Nilai yang bisa berubah
Provider membaca value sekali, saat dibuat. Supaya pembacanya ikut berubah, kirim signal-nya, bukan hasil bacaannya:
value |
Pembacanya menulis | Saat tema diganti |
|---|---|---|
{{ mataUang: "Rp", tema }} |
toko.tema() |
kartu ikut gelap |
{{ mataUang: "Rp", tema: tema() }} |
toko.tema |
tetap terang, karena yang dikirim cuma string "terang" |
Latihan kedua memakai pola yang sama untuk nama pengguna:
import { createContext, useContext, createSignal } from "solid-js"
const UserContext = createContext()
function UserDisplay() {
const ctx = useContext(UserContext)
return <p>User: {ctx.name()}</p>
}
function App() {
const [name, setName] = createSignal("Andi")
return (
<UserContext.Provider value={{ name, setName }}>
<UserDisplay />
</UserContext.Provider>
)
}
value={{ name, setName }}mengirim signal dan setter-nya. Kurung kurawal pertama milik JSX, yang kedua objeknya.ctx.name()dibaca dengan kurung, jadi paragrafnya ikut berubah kalau ada yang memanggilctx.setName("Budi"). HasilnyaUser: Andi.- Nama di paragraf dibaca dari context, bukan diketik
User: Andilangsung. Latihan ini memeriksa ada{sesudah<p>.
Kapan pakai context
Untuk nilai yang dibaca banyak komponen di berbagai kedalaman: tema, mata uang, bahasa, pengguna yang sedang login. Kalau cuma satu anak yang butuh, props lebih jelas. Satu lagi: useContext dipanggil saat komponen jalan. Dipanggil belakangan, misalnya di dalam setTimeout, dia gak tahu lagi posisinya di pohon komponen dan mengembalikan nilai bawaan.
⚠️ Jebakan yang sering ketemu
- Komponen di luar Provider. Dia mendapat nilai bawaan, atau
undefinedkalaucreateContext()dibuat tanpa nilai bawaan. Pastikan komponennya ada di dalam<X.Provider>. value={tema()}. Provider membacanya sekali, jadi pembacanya macet di nilai pertama. Kirim signal-nya:value={{ tema }}, lalu bacatoko.tema().- Nilainya diketik langsung.
<p>User: Andi</p>tampil benar, tapi gak membaca context. Latihan kedua memeriksa paragrafnya merangkai nilai dengan{...}. useContextdipanggil belakangan. Di dalamsetTimeoutatau sesudahawait,useContext(TokoContext)mengembalikan nilai bawaan, walau komponennya ada di dalam Provider. Panggil di badan komponen, simpan hasilnya di variabel, lalu pakai variabel itu.
TL;DR: const ThemeContext = createContext("light") dibuat sekali di luar komponen. <ThemeContext.Provider value="dark"> menyediakan nilai untuk semua yang ada di dalamnya, dan useContext(ThemeContext) membacanya dari Provider terdekat di atas: Tema: dark. Di luar Provider, useContext mengembalikan nilai bawaan dari createContext (atau undefined kalau gak ada). Provider bertingkat: yang terdekat menang. Provider membaca value sekali, jadi kirim signal-nya (value={{ name, setName }}) dan baca dengan kurung (ctx.name()) supaya tampilannya ikut berubah.