Menu

Hook useContext di React: Berbagi Data Tanpa Props

useContext membaca nilai yang disediakan komponen parent, sehingga komponen yang bersarang dalam bisa mendapatkannya tanpa setiap komponen di antaranya meneruskan nilai itu sebagai prop. Pelajari createContext, provider, cara memperbarui context dari child, dan pengaruh context terhadap render ulang.

Halaman ini dilengkapi editor yang bisa dijalankan. Edit, jalankan, dan lihat output-nya seketika.

useContext membaca nilai yang disediakan komponen parent, sehingga komponen yang berada jauh di dalam pohon bisa mendapatkannya tanpa setiap komponen di antaranya meneruskan nilai itu sebagai prop. Kamu membuat context dengan createContext, membungkus sebagian pohon dengan provider yang punya value, lalu memanggil useContext(TheContext) di mana pun kamu butuh nilai itu.

Toolbar sama sekali tidak menyebut tema, tapi kedua tombol berubah saat kamu mencentang kotaknya. Itulah inti dari context: komponen di tengah tidak perlu membawa prop yang tidak mereka pakai, yaitu masalah prop drilling yang diselesaikan context.

Tiga langkahnya

// 1. Create it once, outside any component, usually in its own file
export const ThemeContext = createContext('light');

// 2. Provide a value to a part of the tree
<ThemeContext value={theme}>
    <Page />
</ThemeContext>

// 3. Read it in any component inside that part
const theme = useContext(ThemeContext);

Di React 19, object context itu sendiri adalah provider-nya. Kode lama menulis <ThemeContext.Provider value={theme}>, yang masih berfungsi, jadi kamu akan melihat keduanya.

useContext mencari provider terdekat dari context itu ke atas pohon. Sebuah komponen membaca provider yang paling dekat, jadi kamu bisa menyusun provider bersarang untuk menimpa nilai bagi satu bagian.

Nilai default

Argumen untuk createContext adalah nilai yang dikembalikan useContext saat tidak ada provider di atas komponen. Nilai ini tidak berubah saat state berubah; nilai ini hanya cadangan.

Greeting pertama tidak punya provider di atasnya dan mendapat nilai default 'en'. Yang kedua membaca 'es', dan yang ketiga membaca 'ja' yang lebih dekat. Ganti value="es" di bagian luar menjadi value="ja" dan hanya baris kedua yang berubah.

Memperbarui context dari child

Context hanya meneruskan nilai ke bawah. Agar child yang berada jauh di dalam bisa mengubahnya, simpan nilainya di state tempat provider berada, lalu taruh setter-nya di context di samping nilai itu.

LoginButton memanggil setUser, state di App berubah, provider mendapat nilai baru, dan Header ikut diperbarui. Tidak ada satu pun komponen yang menerima prop.

Setiap consumer dirender ulang saat nilainya berubah

Saat value milik sebuah provider berubah, React merender setiap komponen yang memanggil useContext untuknya, meskipun komponen itu berada di dalam memo dan props-nya tetap sama. React membandingkan nilai lama dan baru dengan Object.is. Jadi value={{ user, setUser }} adalah object baru di setiap render App, dan setiap consumer dirender lagi bahkan saat user tidak berubah.

Kedua reader mencatat log sekali di render pertama. Klik tombolnya: hanya PlainReader yang mencatat lagi, karena context-nya mendapat object baru. useMemo memberikan object yang sama ke MemoContext sampai name berubah, jadi MemoReader tetap diam. Ini baru penting saat rendering mulai lambat; untuk aplikasi kecil, object baru per render tidak masalah.

Memecah context

Nilai yang sering berubah dan nilai yang tidak pernah berubah sebaiknya tidak berbagi satu context. Kalau setter tinggal bersama state-nya, komponen yang hanya perlu memanggil setUser tetap dirender setiap kali user berubah. Taruh keduanya di dua context, dan setiap consumer hanya berlangganan ke apa yang dibacanya:

const UserContext = createContext(null);
const SetUserContext = createContext(() => {});

function UserProvider({ children }) {
    const [user, setUser] = useState(null);
    return (
        <SetUserContext value={setUser}>
            <UserContext value={user}>{children}</UserContext>
        </SetUserContext>
    );
}

setUser dari useState mempertahankan identitas yang sama selamanya, jadi komponen yang hanya membaca SetUserContext tidak pernah dirender karena ada yang login. Ide yang sama berlaku untuk data yang tidak saling berkaitan: tema dan keranjang belanja tempatnya di context terpisah, bukan di satu AppContext besar.

Sentuhan akhir yang umum adalah custom hook seperti useUser() yang memanggil useContext(UserContext) dan melempar error yang jelas saat provider-nya tidak ada, sehingga pemanggil tidak pernah meng-import object context secara langsung.

Context, props, atau library state

  • Props adalah pilihan default. Meneruskan nilai dua atau tiga level ke bawah itu jelas dan mudah dilacak.
  • Context cocok untuk nilai yang dibutuhkan banyak komponen di berbagai kedalaman dan jarang berubah: tema, bahasa, pengguna yang sedang login, object feature flag.
  • Library state (Redux Toolkit, Zustand, Jotai, dan lainnya) cocok untuk state besar yang sering diperbarui, saat kamu ingin sebuah komponen berlangganan ke satu bagian saja dan melewati render yang disebabkan bagian lain. Context tidak punya selector seperti itu: sebuah consumer dirender pada setiap perubahan nilainya.

Sebelum memakai context atau library, periksa apakah meneruskan komponen sebagai children sudah menghilangkan drilling-nya. Sering kali ya, tanpa API baru sama sekali.

Pertanyaan yang sering diajukan

Apa fungsi useContext di React?

useContext mengembalikan nilai terkini dari sebuah context, diambil dari provider terdekat di atas komponen dalam pohon. Saat nilai provider itu berubah, setiap komponen yang membacanya dirender lagi.

Untuk apa nilai default di createContext?

Nilai itu yang dikembalikan useContext saat tidak ada provider di atas komponen. Nilai ini berguna untuk pengujian dan untuk komponen yang dirender sendirian, dan tidak pernah berubah.

Apakah saya masih butuh Context.Provider di React 19?

Tidak. Di React 19 kamu bisa merender context itu sendiri sebagai provider: <ThemeContext value="dark">. <ThemeContext.Provider value="dark"> tetap berfungsi, jadi kode lama tetap berjalan.

Bagaimana cara memperbarui nilai context dari komponen child?

Simpan nilainya di state di komponen yang merender provider, lalu taruh setter-nya di context di samping nilainya: value={{ theme, setTheme }}. Child membaca setTheme dengan useContext lalu memanggilnya.

Apakah React context pengganti Redux?

Context meneruskan nilai ke bawah pohon; context tidak mengelola state sendiri. Digabungkan dengan useState atau useReducer, context mencukupi untuk banyak aplikasi. Library state menambahkan hal-hal yang tidak dimiliki context, seperti berlangganan ke satu bagian saja agar pembaruan yang tidak berkaitan tidak merender ulang komponen, ditambah devtools dan middleware.

Ilustrasi bahasa pemrograman di Coddy

Belajar coding dengan Coddy

Mulai