Setiap pembangun React akhirnya akan menghadapi halangan yang sama. Anda mengambil (fetch) objek pengguna di dalam komponen App peringkat teratas. Kemudian anda menyalurkannya ke bawah. Dan ke bawah lagi. Melalui pembungkus laluan (route wrapper), melalui rangka susun atur (layout shell), melalui bekas sisi (sidebar container), semata-mata supaya komponen avatar kecil yang berada tiga lapisan di dalam boleh memaparkan gambar profil. Komponen di tengah-tengah tidak peduli tentang objek pengguna tersebut. Mereka hanya sekadar menghantar bungkusan tersebut. Itulah prop drilling, dan ia mengubah pokok komponen yang bersih menjadi permainan 'telefon rosak' yang mengecewakan.

Kesukaran sebenar bermula apabila bentuk data tersebut berubah. Mungkin bahagian backend mula menggunakan struktur bersarang user.profile.avatar dan bukannya user.avatar. Tiba-tiba anda perlu menyunting antara muka TypeScript atau PropTypes merentasi lima fail yang tidak pernah menggunakan data tersebut secara langsung. Di situlah React Context API memainkan peranannya.

Bagaimana Context Mengubah Aliran Data

Bayangkan Context sebagai penghala (router) WiFi yang terletak di tengah-tengah rumah anda. Tanpa ia, anda memerlukan kabel Ethernet yang berselirat di setiap bilik untuk mendapatkan isyarat ke komputer riba anda. Dengan adanya ia, penghala tersebut menyiarkan isyarat melalui udara, dan mana-mana peranti dengan kata laluan yang betul boleh menyambung secara terus. Dinding tidak lagi menjadi penghalang.

Dalam istilah React, akar (root) aplikasi anda boleh menyiarkan data melalui pokok komponen tanpa meminta setiap lapisan bertindak sebagai penghantar. Mana-mana komponen bersarang boleh melanggan siaran tersebut dan menerima apa yang diperlukannya secara tepat.

Tiga Komponen Utama

Context API boleh diringkaskan kepada tiga bahagian utama.

React.createContext() menyediakan saluran siaran. Ia mengembalikan satu objek yang mengandungi Provider dan (dalam kod lama) Consumer. Anda hanya perlu memanggil ini sekali untuk sesuatu ciri tertentu.

The Provider ialah komponen yang membungkus satu bahagian pokok komponen anda. Ia menerima satu prop bernama value. Apa sahaja yang anda letakkan dalam prop tersebut akan tersedia untuk setiap keturunan, tidak kira betapa dalamnya kedudukan mereka.

useContext ialah Hook yang membolehkan komponen fungsi mengakses siaran tersebut. Di dalam komponen anda, anda memasukkan objek konteks yang telah dicipta ke dalam useContext, dan ia akan mengembalikan nilai semasa. Itu sahaja. Tiada pembungkus, tiada prop tambahan.

Sebelum kehadiran Hooks, anda perlu menggunakan corak Consumer dengan render props. Ia berfungsi, tetapi ia mewujudkan banyak inden dan kekusutan pembungkus. useContext telah mempermudah semua itu menjadi satu baris tunggal di dalam badan fungsi anda.

Bila Context Benar-benar Perlu Digunakan

Jangan gunakan Context hanya kerana tabiat. Ia dibina untuk data yang dikongsi oleh banyak komponen yang tidak berkaitan merentasi pelbagai cabang pokok komponen anda. Calon yang sesuai termasuk:

  • Tetapan tema. Bukan sekadar mod terang atau gelap, tetapi token jarak (spacing tokens), palet warna, dan skala fon. Menghantar perkara ini secara manual melalui setiap butang dan modal yang digayakan akan menjadi memenatkan dengan cepat.
  • Pengesahan pengguna. Status log masuk, tatasusunan kebenaran (permissions array), atau objek pengguna semasa. Bar pengepala (header bar), widget papan pemuka, dan penjaga laluan peribadi (private route guard) anda mungkin berada di sudut yang berbeza dalam pokok komponen.
  • Pilihan bahasa. String lokal (locale strings), format tarikh, dan simbol mata wang. Komponen daun (leaf components) yang dalam seperti label borang memerlukan perkara ini tanpa setiap induk pada laluan tersebut perlu mengetahuinya.
  • Data bakul belanja. Bilangan item, jumlah nilai, dan fungsi tambah-ke-bakul. Lencana pengepala dan halaman pembayaran memerlukan keadaan (state) yang sama, tetapi mereka biasanya berada di bawah cabang susun atur yang berbeza sama sekali.

Penukar Tema Praktikal

Salah satu cara paling jelas untuk melihat Context berfungsi adalah dengan penukar tema (theme toggle). Berikut adalah cara anda boleh menyambungkannya tanpa melangkau butiran yang penting.

Pertama, cipta fail ThemeContext.js. Panggil React.createContext() dan simpan hasilnya. Kemudian bina komponen ThemeProvider yang menguruskan tema semasa dengan useState atau useReducer. Bungkus komponen anak (children) dalam Provider konteks anda, dengan memasukkan objek yang mengandungi tema semasa dan fungsi untuk menukarnya. Eksport kedua-dua ThemeProvider dan objek konteks itu sendiri.

Kedua, pergi ke titik masuk (entry point) aplikasi anda. Import ThemeProvider dan bungkus keseluruhan aplikasi anda dengannya. Jika anda melangkau langkah ini, apa sahaja yang cuba membaca konteks kemudian hanya akan melihat nilai lalai (default value).

Ketiga, di dalam komponen Header atau Content, import objek konteks dan useContext. Panggil Hook tersebut, lakukan destructuring pada tema dan fungsi penukar, dan gunakan kelas CSS anda secara bersyarat. Tambah butang yang memanggil fungsi penukar tersebut. Komponen tersebut tidak pernah menerima prop theme daripada induknya. Ia menarik isyarat terus dari udara.

Prop Drilling, Context, atau Redux?

Memilih antara alatan ini bukan soal kesetiaan, tetapi lebih kepada bentuk state anda.

Prop drilling adalah sangat sesuai untuk kedalaman dua atau tiga tahap. Ia bersifat eksplisit, mudah dikesan dalam IDE anda, dan mengekalkan kebergantungan (dependencies) yang jelas. Masalah hanya akan timbul apabila anda mula menghantar prop yang sama melalui enam atau tujuh lapisan.

Context API disertakan bersama React itu sendiri. Ini bermakna tiada saiz bundle tambahan dan tiada penyediaan luaran. Ia mengendalikan state global bersaiz kecil hingga sederhana dengan baik, terutamanya data yang jarang berubah seperti tema atau profil pengguna.

Redux memerlukan pemasangan perpustakaan (libraries) tambahan dan penulisan boilerplate. Ia sangat berbaloi apabila logik state anda kompleks, apabila pelbagai bahagian (slices) state berinteraksi secara mendalam, atau apabila anda memerlukan debugging 'time-travel' dan middleware. Untuk data global yang ringkas, Redux adalah berlebihan (overkill).

Realiti Prestasi yang Tiada Sesiapa Bincangkan

Inilah perbezaan yang memisahkan pelaksanaan tahap junior daripada senior. Apabila nilai Context Provider berubah, setiap komponen yang menggunakan context tersebut akan melakukan render semula (re-render). Tidak kira sama ada bahagian (slice) khusus yang diperlukan oleh komponen itu kekal sama. React mengesan rujukan (reference) baharu dan menjadualkan kemas kini.

Jika anda memasukkan keseluruhan state aplikasi ke dalam satu StoreContext yang besar, anda secara berkesan telah menyatukan seluruh UI anda. Menukar tetapan tema akan menyebabkan render semula pada troli beli-belah, carta papan pemuka (dashboard), dan senarai notifikasi anda. Itu adalah kerja yang tidak perlu.

Pecahkan context anda mengikut domain. Simpan ThemeContext untuk tetapan visual, UserContext untuk data profil, dan CartContext untuk state perdagangan. Jika pengguna mengedit nama paparan mereka, header anda akan dikemas kini tanpa mengganggu grid produk. Selain itu, berhati-hati dengan apa yang anda masukkan ke dalam prop value Provider. Jika anda memasukkan literal objek { theme, toggleTheme } secara dalam talian (inline) semasa render, anda mencipta rujukan baharu pada setiap render dan mencetuskan kemas kini yang tidak perlu. Stabilkan bentuk tersebut dengan useMemo jika nilai tersebut mengandungi fungsi atau data bukan primitif.

Kesilapan yang Membazirkan Masa Berjam-jam

Dua ralat sering memerangkap pasukan berulang kali.

Lupa mengeksport objek context. Adalah mudah untuk mengeksport komponen ThemeProvider dan kemudian cuba memanggil useContext(ThemeProvider). Begitulah bukan cara ia berfungsi. Hook tersebut memerlukan objek context yang dikembalikan oleh createContext, bukannya komponen pembungkus (wrapper). Jika anda hanya mengeksport Provider, pengguna (consumers) anda tidak mempunyai apa-apa untuk diimport.

Memanggil useContext di luar Providernya. Hook tersebut mengembalikan nilai lalai yang anda berikan kepada createContext. Jika anda tidak memberikan nilai lalai, anda akan mendapat undefined. Jika pokok komponen (component tree) anda merender pengguna lebih tinggi dalam DOM berbanding Provider, atau jika Provider hilang sepenuhnya, data anda tidak akan sampai. Semak semula sama ada fail index atau root anda benar-benar membungkus aplikasi tersebut.

Kesimpulan Sebenar

React Context bukanlah satu revolusi pengurusan state. Ia adalah alat khusus untuk masalah spatial tertentu: menghantar data ke komponen yang jauh tanpa menukarkan setiap lapisan menjadi pejabat pos. Gunakannya untuk data yang benar-benar global, pastikan context anda dipecahkan mengikut domain untuk melindungi prestasi render, dan sentiasa bungkus pokok komponen anda dengan Provider yang betul sebelum anda cuba membaca isyarat tersebut. Kuasai tabiat ini, dan pokok komponen anda akan kekal bersih, pantas, dan mudah difahami.