Di React, konteks adalah cara untuk meneruskan data antar pohon komponen tanpa menambahkan props secara manual untuk setiap lapisan komponen; konteks menyediakan cara untuk berbagi nilai tertentu antar komponen tanpa harus meneruskannya secara eksplisit melalui komponen di pohon.
Lingkungan operasi tutorial ini: Sistem Windows 10, reaksi versi 17.0.1, komputer Dell G3.
Konteks menyediakan cara untuk meneruskan data antar pohon komponen tanpa menambahkan props secara manual ke setiap lapisan komponen. Dalam aplikasi React pada umumnya, data diteruskan dari atas ke bawah (dari induk ke anak) melalui props, namun pendekatan ini sangat rumit untuk tipe properti tertentu (misalnya preferensi lokal, tema UI), properti ini diperlukan oleh banyak komponen di aplikasi. Konteks menyediakan cara untuk berbagi nilai-nilai tersebut antar komponen tanpa harus secara eksplisit meneruskan props melalui setiap level pohon komponen.
Konteks dirancang untuk berbagi data yang bersifat "global" ke pohon komponen, seperti pengguna, tema, atau bahasa pilihan yang saat ini diautentikasi. Misalnya, dalam kode berikut, kita secara manual menyesuaikan gaya komponen tombol melalui atribut "tema".
class App extends React.Component { render() { return <Toolbar theme="dark" />; }}function Toolbar(props) { // Komponen Toolbar menerima properti "tema" tambahan, yang kemudian diteruskan ke ThemedButton komponen. // Akan merepotkan jika setiap tombol dalam aplikasi perlu mengetahui nilai tema, // karena nilai ini harus diturunkan ke semua komponen. return ( <p> <ThemedButton theme={props.theme} /> </p> );}class ThemedButton extends React.Component { render() { return <Button theme={this.props.theme} /> ; }// Melewati props: App -> Toolbar -> ThemedButton// Jika sarangnya sangat dalam, maka props harus dilewati lapis demi lapis, meskipun props tidak diperlukan di tengah, sepertinya sangat rumit.Dengan menggunakan konteks, kita dapat menghindari melewatkan props melalui elemen perantara
// Konteks memungkinkan kita meneruskan nilai jauh ke dalam pohon komponen tanpa harus meneruskannya secara eksplisit ke setiap komponen. // Membuat konteks untuk tema saat ini ("cahaya" adalah nilai default). const ThemeContext = React.createContext('light');class App extends React.Component { render() { // Gunakan Penyedia untuk meneruskan tema saat ini ke pohon komponen berikut. // Tidak peduli seberapa dalam, komponen apa pun dapat membaca nilai ini. // Dalam contoh ini, kita meneruskan "gelap" sebagai nilai saat ini. return ( <ThemeContext.Provider value="dark"> <Toolbar /> </ThemeContext.Provider> }}// Komponen perantara tidak perlu lagi menentukan tema yang akan diturunkan. function Toolbar() { return ( <p> <ThemedButton /> </p> );}class ThemedButton extends React.Component { // Tentukan tipe konteks untuk membaca konteks tema saat ini. // React akan mencari penyedia tema terdekat dan menggunakan nilainya. // Dalam contoh ini, nilai tema saat ini adalah "gelap". staticcontextType = ThemeContext; render() { return <Button theme={this.context} }}// Anda juga dapat menggunakan ThemedButto.contextType = ThemeContext;Buat objek Konteks. Ketika React merender sebuah komponen yang berlangganan objek Konteks ini, komponen tersebut akan membaca nilai konteks saat ini dari Penyedia yang paling cocok dengan dirinya di pohon komponen.
Hanya jika tidak ada Penyedia yang cocok di pohon tempat komponen berada, parameter defaultValue akan berlaku. Ini membantu dalam menguji komponen tanpa menggunakan Penyedia untuk membungkus komponen. Catatan: Ketika undefinisi diteruskan ke nilai Penyedia, defaultValue dari komponen yang menggunakan tidak akan berpengaruh.
Setiap objek Context mengembalikan komponen Provider React, yang memungkinkan komponen yang menggunakan untuk berlangganan perubahan konteks.
Penyedia menerima atribut nilai dan meneruskannya ke komponen konsumsi. Penyedia dapat memiliki hubungan yang sesuai dengan beberapa komponen konsumen. Beberapa Penyedia juga dapat digunakan secara bertingkat, dan lapisan dalam akan menimpa data lapisan luar.
Ketika nilai Penyedia berubah, semua komponen yang mengkonsumsi di dalamnya akan dirender ulang. Baik Penyedia maupun komponen konsumen internalnya tidak tunduk pada fungsi mustComponentUpdate, sehingga komponen konsumen dapat diperbarui meskipun komponen leluhurnya tidak lagi diperbarui.
Atribut konteksType yang dipasang pada kelas akan dipindahkan ke objek Konteks yang dibuat oleh React.createContext(). Ini memungkinkan Anda menggunakan this.context untuk menggunakan nilai pada Konteks terbaru. Anda dapat mengaksesnya dalam siklus hidup apa pun, termasuk fungsi render
import MyContext from './MyContext';class MyClass extends React.Component { componentDidMount() { let value = this.context /* Setelah komponen dipasang, gunakan nilai komponen MyContext untuk melakukan beberapa operasi efek samping*; / } komponenDidUpdate() { biarkan nilai = ini.konteks; /* ... */ } komponenWillUnmount() { biarkan nilai = ini.konteks; ; /* Render berdasarkan nilai komponen MyContext*/ } // Atau gunakan staticcontextType = MyContext;}MyClass.contextType = MyContext;Di sini, komponen React juga dapat mengikuti perubahan konteks. Hal ini memungkinkan Anda untuk berlangganan konteks dalam komponen fungsional.
Hal ini memerlukan fungsi sebagai seorang anak. Fungsi ini menerima nilai konteks saat ini dan mengembalikan node React. Nilai yang diteruskan ke fungsi setara dengan nilai yang diberikan oleh Penyedia yang paling dekat dengan konteks ini di pohon komponen. Jika tidak ada Penyedia yang sesuai, parameter nilai setara dengan defaultValue yang diteruskan ke createContext().
Objek konteks menerima properti bernama displayName, yang bertipe string. React DevTools menggunakan string ini untuk menentukan konteks apa yang akan ditampilkan.
Komponen berikut akan muncul sebagai MyDisplayName di DevTools
const MyContext = React.createContext(/* beberapa nilai */);MyContext.displayName = 'MyDisplayName';<MyContext.Provider> // "MyDisplayName.Provider" di DevTools <MyContext.Consumer> // "MyDisplayName.Consumer" Di Alat PengembangUntuk contoh tema di atas, penggunaan yang lebih kompleks dapat dicapai dengan menggunakan nilai dinamis.
tema-konteks.js
ekspor const tema = { terang: { latar depan: '#000000', latar belakang: '#eeeeee', }, gelap: { latar depan: '#ffffff', latar belakang: '#222222', },};ekspor const ThemeContext = Bereaksi .createContext(themes.dark); // Ini adalah nilai defaultbertema-button.js
import { ThemeContext } from './theme-context'; class ThemedButton extends React.Component { render() { let props = this.props; // Dapatkan nilai default di ThemeContext let theme = this.context; {...props} style={{backgroundColor: theme.background}} /> } // staticcontextType = ThemeContext;}ThemedButton.contextType = ThemeContext;ekspor default ThemedButton;aplikasi.js
import { ThemeContext, tema } dari './theme-context';import ThemedButton dari './themed-button';// Fungsi komponen perantara menggunakan ThemedButton Toolbar(props) { return ( <ThemedButton onClick={props.changeTheme } > Ubah Tema </ThemedButton> );} class Aplikasi extends React.Component { konstruktor(props) { super(props); .setState(state => ({ theme: state.theme === Themes.dark ? Themes.light : Themes.dark, }) }; // Digunakan oleh komponen tombol ThemedButton di dalam ThemeProvider Tema nilai dalam keadaan, // dan komponen eksternal menggunakan nilai tema default yang dikembalikan ( <Page> <ThemeContext.Provider value={this.state.theme}> <Toolbar changeTheme={this.toggleTheme} /> </ThemeContext .Provider> <Section> <ThemedButton /> </Section> </Page> }}ReactDOM.render(<App />, document.root);// Komponen yang dibungkus oleh ThemeContext.Provider dapat menggunakan ThemeContext Nilai // di Toolbar dan ThemedButton dapat menggunakan this.context untuk mendapatkan nilai // Perhatikan bahwa metode memperbarui status adalah dengan meneruskannya melalui props, dan pembaruan dipicu oleh komponen turunan. Metode melalui konteks akan dibahas di bawahDalam contoh di atas, kita meneruskan fungsi update melalui props untuk mengubah nilai tema di App. Kita tahu bahwa konteks dari komponen yang bersarang dalam di pohon komponen perlu diperbarui. Dalam skenario ini, Anda dapat meneruskan fungsi melalui konteks untuk membuat komponen konsumen memperbarui konteksnya.
tema-konteks.js
// Pastikan struktur data nilai default yang diteruskan ke createContext cocok dengan komponen pemanggil (konsumen)! ekspor const ThemeContext = React.createContext({ tema: tema.dark, toggleTheme: () => {}, // Tentukan metode untuk memperbarui tema, sebarkan});tema-toggler-button.js
import { ThemeContext } from './theme-context';function ThemeTogglerButton() { // Tombol Theme Toggler tidak hanya mendapatkan nilai tema, namun juga mendapatkan fungsi toggleTheme dari konteks (bagian app.js di bawah) return ( < ThemeContext.Consumer> {({theme, toggleTheme}) => ( <button onClick={toggleTheme} style={{backgroundColor: theme.background}}> Alihkan Tema </button> )} </ThemeContext.Consumer> ); }ekspor ThemeTogglerButton bawaan;aplikasi.js
import { ThemeContext, tema } dari './theme-context';import ThemeTogglerButton dari './theme-toggler-button';class App extends React.Component { konstruktor(props) { super(props); ) => { this.setState(state => ({ theme: state.theme === Themes.dark ? Themes.light : Themes.dark, })); // State juga berisi fungsi update, jadi Akan diteruskan ke penyedia konteks. this.state = { theme: theme.light, toggleTheme: this.toggleTheme, // Tentukan fungsi pembaruan dan teruskan melalui konteks } } render() { // Seluruh status diteruskan ke penyedia return ( < Nilai Penyedia ThemeContext={ini.status}> <Konten /> </ThemeContext.Provider> }}fungsi Konten() { return ( <p> <ThemeTogglerButton /> </p> );}ReactDOM.render( <Aplikasi />, dokumen.root);Untuk memastikan bahwa konteks dirender ulang dengan cepat, React perlu menjadikan konteks setiap komponen konsumen sebagai node terpisah di pohon komponen.
// Konteks tema, tema defaultnya adalah nilai "ringan" const ThemeContext = React.createContext('light'); // Konteks login pengguna const UserContext = React.createContext({ name: 'Guest',}); React .Component { render() { const { signInUser, theme } = this.props; // Komponen aplikasi yang memberikan nilai konteks awal ( <ThemeContext.Provider value={theme}> <UserContext.Provider value={signedInUser} > < Tata Letak /> </UserContext.Provider> </ThemeContext.Provider> }}fungsi Tata Letak() { return ( <p> <Sidebar /> <Konten /> </p> );}// Sebuah komponen boleh menggunakan beberapa fungsi konteks Content() { return ( <ThemeContext.Consumer> {theme => ( <UserContext.Consumer> {user => ( <ProfilePage user={user} theme={theme} /> )} </UserContext . Konsumen> )} </ThemeContext.Konsumen> );}Jika dua atau lebih nilai konteks sering digunakan bersama-sama, Anda mungkin ingin mempertimbangkan untuk membuat komponen render Anda sendiri untuk menyediakan nilai-nilai ini.
Karena konteksnya menggunakan identitas referensi untuk memutuskan waktu rendering, mungkin ada beberapa kendala yang dapat memicu rendering tak terduga di komponen konsumen saat komponen induk penyedia melakukan rendering ulang. Misalnya, setiap kali Penyedia dirender ulang, kode berikut akan merender ulang semua komponen konsumen berikut, karena atribut value selalu ditetapkan ke objek baru
Aplikasi kelas extends React.Component { render() { return ( <MyContext.Provider value={{something: 'something'}}> <Toolbar /> </MyContext.Provider> }}Untuk mencegah situasi ini, status nilai dipromosikan ke status node induk.
class App extends React.Component { konstruktor(props) { super(props); // Setelah beberapa kali rendering, status akan dipertahankan. Ketika nilainya tidak berubah, komponen konsumen berikut tidak akan merender ulang this.state = { nilai: { sesuatu: 'sesuatu'}, }; } render() { kembali ( <Nilai penyedia={ini.status.nilai}> <Bilah Alat /> </Penyedia> }}