TL; DR
Apakah Konteks dan Redux sama?
Tidak. Mereka adalah alat berbeda yang melakukan hal berbeda dan digunakan untuk tujuan berbeda.
Apakah konteks merupakan alat untuk "manajemen negara"?
Tidak. Konteks adalah salah satu bentuk injeksi ketergantungan. Ini adalah mekanisme transportasi yang tidak mengontrol apa pun. Setiap "manajemen keadaan" dilakukan secara manual, biasanya menggunakan hook useState () / useReducer ().
Apakah Context dan useReducer () pengganti Redux?
Tidak. Mereka agak mirip dan sebagian tumpang tindih, tetapi sangat berbeda dalam hal kemampuan.
Kapan sebaiknya Anda menggunakan konteks?
Saat Anda ingin membuat beberapa data tersedia untuk beberapa komponen, tetapi Anda tidak ingin meneruskan data itu sebagai props di setiap level pohon komponen.
Kapan Anda harus menggunakan Context dan useReducer ()?
Saat Anda perlu mengelola status komponen yang cukup kompleks di bagian tertentu aplikasi Anda.
Kapan Anda harus menggunakan Redux?
Redux paling berguna jika:
- Sejumlah besar komponen berstatus menggunakan data yang sama
- Status aplikasi sering diperbarui
- Logika kompleks untuk memperbarui status
- Aplikasi ini memiliki basis kode sedang hingga besar dan banyak orang sedang mengerjakannya
- Anda ingin tahu kapan, mengapa, dan bagaimana status aplikasi diperbarui dan dapat memvisualisasikan perubahan tersebut
- Anda membutuhkan kemampuan yang lebih kuat untuk menangani efek samping, stabilitas (persistensi), dan serialisasi data
Memahami Konteks dan Redux
Untuk menggunakan alat ini dengan benar, penting untuk memahami:
- Untuk apa ini
- Tugas apa yang dipecahkannya
- Kapan dan mengapa itu dibuat
Penting juga untuk memahami masalah apa yang Anda coba selesaikan dalam aplikasi Anda dan menggunakan alat yang paling baik menyelesaikannya, bukan karena seseorang menyuruh Anda untuk menggunakannya, dan bukan karena mereka populer, tetapi karena mereka bekerja paling baik untuk Anda dalam situasi khusus.
Kebingungan seputar Konteks dan Redux terutama disebabkan oleh kesalahpahaman tentang tujuan alat-alat ini dan tugas apa yang mereka selesaikan. Oleh karena itu, sebelum berbicara tentang kapan mereka harus digunakan, perlu ditentukan apa itu dan masalah apa yang mereka selesaikan.
Apa konteksnya?
Mari kita mulai dengan mendefinisikan konteks dari dokumentasi resmi :
“Konteks memungkinkan data diteruskan melalui pohon komponen tanpa harus melewati props di tingkat menengah.
Dalam aplikasi React yang khas, data diteruskan dari atas ke bawah (dari induk ke anak) menggunakan props. Namun, metode ini bisa berlebihan untuk beberapa jenis props (misalnya, bahasa yang dipilih, tema antarmuka) yang perlu diteruskan ke banyak komponen dalam aplikasi. Konteks menyediakan cara untuk mendistribusikan data tersebut di antara komponen tanpa harus secara eksplisit meneruskan props di setiap level pohon komponen. "
Harap dicatat bahwa definisi ini tidak mengatakan sepatah kata pun tentang "manajemen", hanya tentang "transfer" dan "distribusi".
API konteks saat ini (React.createContext ()) pertama kali diperkenalkan di React 16.3 sebagai pengganti API lama yang tersedia di versi React sebelumnya, tetapi dengan beberapa kekurangan desain. Salah satu masalah utama adalah bahwa pembaruan ke nilai yang diteruskan melalui konteks dapat "diblokir" jika komponen melewatkan rendering melalui shouldComponentUpdate (). Karena banyak komponen yang menggunakan shouldComponentUpdate () untuk tujuan pengoptimalan, meneruskan data melalui konteks menjadi tidak berguna. createContext () dirancang untuk mengatasi masalah ini, jadi setiap pembaruan nilai akan tercermin dalam komponen turunan, bahkan jika komponen perantara melewatkan rendering.
Menggunakan konteks
Menggunakan konteks dalam aplikasi mengasumsikan hal berikut:
- Panggil const MyContext = React.createContext () untuk membuat instance objek konteks
- Dalam komponen induk, render & ltMyContext.Provider value = {someValue}>. Ini menempatkan beberapa data ke dalam konteks. Data ini bisa apa saja: string, number, object, array, instance kelas, event handler, dll.
- Dapatkan nilai konteks di setiap komponen di dalam penyedia dengan memanggil const theContextValue = useContext (MyContext)
Ketika komponen induk diperbarui dan referensi baru diteruskan sebagai nilai penyedia, setiap komponen yang "menggunakan" konteks akan dipaksa untuk diperbarui.
Biasanya, nilai konteks adalah status komponen:
import { createContext } from 'react'
export const MyContext = createContext()
export function ParentComponent({ children }) {
const [counter, setCounter] = useState(0)
return (
<MyContext.Provider value={[counter, setCounter]}>
{children}
</MyContext.Provider>
)
}
Komponen anak kemudian bisa memanggil hook useContext () dan membaca nilai konteks:
import { useContext } from 'react'
import { MyContext } from './MyContext'
export function NestedChildComponent() {
const [counter, setCounter] = useContext(MyContext)
// ...
}
Kita dapat melihat bahwa konteks sebenarnya tidak mengontrol apapun. Sebaliknya, itu semacam pipa. Anda meletakkan data di awal (atas) terowongan menggunakan <MyContext.Provider>, lalu data ini akan diturunkan hingga komponen memintanya menggunakan useContext (MyContext).
Jadi, tujuan utama dari konteks ini adalah untuk mencegah pengeboran penyangga. Alih-alih meneruskan data sebagai props di setiap level pohon komponen, semua komponen yang bersarang di <MyContext.Provider> bisa mengaksesnya melalui useContext (MyContext). Ini menghilangkan kebutuhan untuk menulis kode untuk mengimplementasikan logika pengoperan prop.
Secara konseptual, ini adalah bentuk injeksi ketergantungan... Kita tahu bahwa anak membutuhkan data jenis tertentu, tetapi tidak mencoba membuat atau menyetel data ini sendiri. Sebaliknya, ini bergantung pada beberapa leluhur untuk meneruskan data ini pada waktu proses.
Apa itu Redux?
Inilah yang dikatakan definisi Dasar-Dasar Redux :
“Redux adalah pola desain dan pustaka untuk mengelola dan memperbarui status aplikasi menggunakan peristiwa yang disebut operasi. Redux bertindak sebagai repositori terpusat dari status aplikasi, mengikuti aturan untuk memastikan pembaruan status yang dapat diprediksi.
Redux memungkinkan Anda untuk mengelola status "global" - status yang disalurkan ke beberapa bagian aplikasi Anda.
Pola dan alat yang disediakan oleh Redux memudahkan untuk menentukan di mana, kapan, mengapa, dan bagaimana status diperbarui dan bagaimana aplikasi menanggapi perubahan itu. ”
Harap dicatat bahwa deskripsi ini menunjukkan:
- Manajemen negara
- Tujuan Redux adalah untuk menentukan mengapa dan bagaimana perubahan status terjadi
Redux awalnya merupakan implementasi dari "arsitektur Flux" , pola desain yang dikembangkan oleh Facebook pada tahun 2014, satu tahun setelah React dirilis. Sejak munculnya Flux, komunitas telah mengembangkan banyak pustaka yang menerapkan konsep ini dengan cara yang berbeda. Redux muncul pada tahun 2015 dan dengan cepat menjadi pemenang kompetisi ini berkat desainnya yang cermat, memecahkan masalah umum, dan kompatibilitas yang sangat baik dengan React.
Secara arsitektural, Redux dengan tegas menggunakan prinsip-prinsip pemrograman fungsional, yang memungkinkan Anda untuk menulis kode dalam bentuk "reduksi" yang dapat diprediksi, dan memisahkan gagasan "apa yang terjadi" dari logika yang mendefinisikan "bagaimana status diperbarui saat ini acara terjadi. " Redux juga menggunakan middleware sebagai cara untuk memperluas kemampuan penyimpanan, termasuk menangani efek samping .
Redux juga menyediakan alat pengembang untuk menjelajahi sejarah operasi dan perubahan status dari waktu ke waktu.
Redux dan React
Redux sendiri tidak bergantung pada UI - Anda dapat menggunakannya dengan lapisan tampilan apa pun (React, Vue, Angular, vanilla JS, dll.) Atau tanpa UI sama sekali.
Namun, paling sering, Redux digunakan bersama dengan React. Pustaka React Redux adalah lapisan pengikat UI resmi yang memungkinkan komponen React berinteraksi dengan penyimpanan Redux dengan mengambil nilai dari status Redux dan memulai operasi. React-Redux menggunakan konteks secara internal. Namun, perlu dicatat bahwa React-Redux melewati konteks instance penyimpanan Redux, bukan nilai status saat ini!Ini adalah contoh penggunaan konteks untuk injeksi ketergantungan. Kita tahu bahwa komponen yang terhubung ke Redux perlu berinteraksi dengan penyimpanan Redux, tetapi kita tidak tahu atau tidak peduli penyimpanan apa itu ketika kita mendefinisikan komponen. Penyimpanan Redux yang sebenarnya dimasukkan ke dalam pohon saat runtime menggunakan komponen <Provider> yang disediakan oleh React-Redux.
Oleh karena itu, React-Redux juga dapat digunakan untuk mencegah "pengeboran" (karena penggunaan konteks internal). Alih-alih meneruskan nilai baru secara eksplisit melalui <MyContext.Provider>, kita bisa memasukkan data ini ke penyimpanan Redux dan kemudian mengambilnya di komponen yang diinginkan.
Tujuan dan Kasus Penggunaan (React-) Redux
Tujuan utama Redux menurut dokumentasi resmi:
"Pola dan alat yang disediakan oleh Redux memudahkan untuk memahami kapan, di mana, mengapa, dan bagaimana perubahan status terjadi, dan bagaimana aplikasi bereaksi terhadapnya."
Ada beberapa alasan lagi untuk menggunakan Redux. Salah satu alasannya adalah untuk mencegah "pengeboran".
Kasus penggunaan lainnya:
- Pemisahan lengkap dari logika manajemen negara dan lapisan UI
- Distribusi logika manajemen negara antara berbagai lapisan UI (misalnya, dalam proses menerjemahkan aplikasi dari AngularJS ke React)
- Menggunakan middleware Redux untuk menambahkan logika ekstra saat menginisialisasi operasi
- Kemampuan untuk menyimpan bagian dari status Redux
- Kemampuan untuk menerima laporan bug yang dapat direproduksi oleh pengembang lain
- Kemampuan untuk men-debug logika dan UI dengan cepat selama pengembangan
Dan Abramov mencantumkan kasus-kasus ini dalam artikelnya tahun 2016, Why You May Not Need Redux .
Mengapa konteks bukan alat untuk "manajemen negara"?
Status adalah setiap data yang menggambarkan perilaku suatu aplikasi . Kita dapat mengkategorikan negara ke dalam kategori seperti status server, status komunikasi, dan status lokal jika kita mau, tetapi aspek utamanya adalah menyimpan, membaca, memperbarui, dan menggunakan data.
David Khourshid, penulis perpustakaan XState dan spesialis manajemen negara, mencatat dalam salah satu tweetnya bahwa:
"Manajemen negara adalah tentang mengubah keadaan dari waktu ke waktu."
Jadi, kita dapat mengatakan bahwa "manajemen negara" berarti sebagai berikut:
- Menyimpan nilai awal
- Mendapatkan nilai saat ini
- Memperbarui nilai
Juga, biasanya ada cara untuk mendapatkan pemberitahuan ketika nilai status saat ini telah berubah.
React hooks useState () dan useReducer () adalah contoh bagus dari manajemen status. Dengan pengait ini kita bisa:
- Simpan nilai awal dengan memanggil hook
- Dapatkan nilai saat ini juga dengan memanggil hook
- Perbarui nilai dengan memanggil setState () atau dispatch (), masing-masing
- Waspadai pembaruan status dengan merender ulang komponen
Redux dan MobX juga memungkinkan Anda mengelola status:
- Redux menyimpan nilai awal dengan memanggil root reducer, memungkinkan membaca nilai saat ini dengan store.getState (), memperbarui nilai dengan store.dispatch (action), dan menerima pemberitahuan pembaruan status melalui store.subscribe (listener)
- MobX mempertahankan nilai awal dengan menetapkan nilai ke bidang kelas penyimpanan, memungkinkan nilai saat ini untuk dibaca dan diperbarui melalui bidang penyimpanan, dan menerima pemberitahuan pembaruan status menggunakan metode autorun () dan computed ()
Alat manajemen status bahkan menyertakan alat cache server seperti React-Query, SWR, Apollo dan Urql - mereka menyimpan nilai awal berdasarkan data yang diambil, mengembalikan nilai saat ini menggunakan hook, dan memungkinkan memperbarui nilai melalui "mutasi server" dan memberi tahu perubahan dengan merender ulang komponen.
Konteks React tidak memenuhi kriteria yang disebutkan. Oleh karena itu, ini bukan alat manajemen negara
Seperti disebutkan sebelumnya, konteks itu sendiri tidak menyimpan apa pun. Komponen induk, yang merender <MyContext.Provider>, bertanggung jawab untuk meneruskan nilai, yang biasanya bergantung pada status komponen. "State management" yang sebenarnya berasal dari hook useState () / useReducer ().
David Khourshid juga mencatat:
“Konteks adalah bagaimana keadaan yang ada dibagi antar komponen. Konteks tidak berpengaruh apa-apa dengan negara. "
Dan dalam tweet berikutnya ,
"Saya kira konteksnya seperti alat peraga tersembunyi yang keadaan abstrak."
Apa pun yang dilakukan konteks adalah menghindari "pengeboran".
Membandingkan Konteks dan Redux
Mari bandingkan kemampuan konteks dan React + Redux:
- Konteks
-
- Tidak menyimpan apa pun dan tidak mengelola apa pun
- Hanya bekerja di komponen React
- Meneruskan di bawah nilai sederhana (tunggal), yang bisa berupa apa saja (primitif, objek, kelas, dll.)
- Mari membaca makna sederhana ini
- Dapat digunakan untuk mencegah "pengeboran"
- Menunjukkan nilai saat ini untuk komponen Penyedia dan Konsumen di alat pengembang, tetapi tidak menunjukkan riwayat perubahan nilai ini
- Pembaruan memakan komponen ketika nilai berubah, tetapi tidak memungkinkan pembaruan dilewati
- Tidak menyediakan mekanisme untuk menangani efek samping - hanya bertanggung jawab untuk rendering
- Bereaksi + Redux
-
- Menyimpan dan mengelola nilai sederhana (biasanya sebuah objek)
- Bekerja dengan UI apa pun serta di luar komponen React
- Mari membaca makna sederhana ini
- Dapat digunakan untuk mencegah "pengeboran"
- Dapat memperbarui nilai dengan menginisialisasi operasi dan menjalankan reduksi
- Alat pengembang menunjukkan riwayat inisialisasi operasi dan perubahan status
- Menyediakan kemampuan untuk menggunakan middleware untuk menangani efek samping
- Mengizinkan komponen berlangganan pembaruan penyimpanan, mengambil bagian tertentu dari status penyimpanan, dan mengontrol rendering ulang komponen
Jelas, ini adalah alat yang sangat berbeda dengan kemampuan berbeda. Satu-satunya titik persimpangan di antara mereka adalah untuk mencegah "pengeboran".
Konteks dan kegunaanReducer ()
Salah satu masalah dengan diskusi "Konteks versus Redux" adalah bahwa orang sering kali bermaksud, "Saya menggunakan useReducer () untuk mengelola status dan konteks untuk menyampaikan nilai." Tapi sebaliknya, mereka hanya berkata, "Saya menggunakan konteks." Ini, menurut pendapat saya, adalah alasan utama dari kebingungan yang berkontribusi pada pemeliharaan mitos bahwa konteks "mengontrol negara".
Pertimbangkan kombinasi Context + useReducer (). Ya, kombinasi ini terlihat sangat mirip dengan Redux + React-Redux. Kedua kombinasi ini memiliki:
- Nilai tersimpan
- Fungsi peredam
- Kemampuan untuk menginisialisasi operasi
- Kemampuan untuk meneruskan nilai dan membacanya di komponen bersarang
Namun demikian, masih terdapat beberapa perbedaan penting di antara mereka yang terwujud dalam kemampuan dan perilakunya. Saya telah mencatat perbedaan ini dalam React, Redux, dan Context Behavior dan The (Almost) Complete Guide to Rendering in React . Singkatnya, berikut ini dapat dicatat:
- Context + useReducer () mengandalkan penerusan nilai saat ini melalui konteks. React-Redux meneruskan instance penyimpanan Redux saat ini melalui konteks
- Artinya, saat useReducer () menghasilkan nilai baru, semua komponen yang berlangganan konteks dipaksa untuk menggambar ulang, meskipun mereka hanya menggunakan beberapa data. Hal ini dapat menyebabkan masalah kinerja tergantung pada ukuran nilai status, jumlah komponen yang ditandatangani, dan frekuensi rendering ulang. Dengan React-Redux, komponen dapat berlangganan ke bagian tertentu dari nilai penyimpanan dan hanya menggambar ulang ketika bagian itu berubah
Ada perbedaan penting lainnya:
- Context + useReducer () adalah kapabilitas bawaan dari React dan tidak dapat digunakan di luarnya. Toko Redux adalah UI agnostik, sehingga dapat digunakan secara terpisah dari React
- React DevTools , . Redux DevTools , ( , type and payload),
- useReducer() middleware. useEffect() useReducer(), useReducer() middleware, Redux middleware
Inilah yang dikatakan Sebastian Markbage (Arsitek Tim Inti React) tentang penggunaan konteks :
“Pendapat pribadi saya adalah bahwa konteks baru siap digunakan untuk pembaruan frekuensi rendah yang tidak terduga (seperti lokalisasi atau tema). Ini juga dapat digunakan di semua kasus di mana konteks lama digunakan, mis. untuk nilai statis dengan distribusi pembaruan berikutnya dengan berlangganan. Ini belum siap untuk digunakan sebagai pengganti distributor negara seperti Flux. "
Ada banyak artikel di web yang menyarankan untuk menyiapkan beberapa konteks terpisah untuk berbagai bagian negara bagian, menghindari perenderan ulang yang tidak perlu dan menyelesaikan masalah pelingkupan. Beberapa pos juga menyarankan untuk menambahkan "komponen kontekstual" Anda sendiri , yang memerlukan kombinasi React.memo (), useMemo (), dan dengan rapi membagi kode menjadi dua konteks untuk setiap bagian aplikasi (satu untuk data, satu untuk fungsi pembaruan). Tentu saja, Anda dapat menulis kode dengan cara ini, tetapi dalam kasus ini Anda menciptakan kembali React-Redux.
Jadi sementara Context + useReducer () adalah alternatif ringan untuk Redux + React-Redux dalam perkiraan pertama ... kombinasi ini tidak identik, konteks + useReducer () tidak dapat sepenuhnya menggantikan Redux!
Memilih alat yang tepat
Untuk memilih alat yang tepat, sangat penting untuk memahami tugas apa yang diselesaikan alat tersebut, serta tugas apa yang Anda hadapi.
Gunakan Ikhtisar Kasus
- Konteks
- Transfer data ke komponen bersarang tanpa "pengeboran"
- useReducer ()
- Mengontrol keadaan komponen kompleks menggunakan fungsi peredam
- Konteks + penggunaanReducer ()
- Mengelola status komponen kompleks menggunakan fungsi peredam dan mentransfer status ke komponen bersarang tanpa "mengebor"
- Redux
- Mengontrol keadaan yang sangat kompleks dengan fungsi peredam
- Ketertelusuran kapan, mengapa dan bagaimana keadaan berubah seiring waktu
- Keinginan untuk sepenuhnya mengisolasi logika manajemen negara dari lapisan UI
- Mendistribusikan logika manajemen keadaan antara berbagai lapisan UI
- Menggunakan kemampuan middleware untuk mengimplementasikan logika tambahan saat menginisialisasi operasi
- Kemampuan untuk menyelamatkan bagian tertentu dari negara
- Kemampuan untuk mendapatkan laporan kesalahan yang dapat direproduksi
- Kemampuan untuk men-debug logika dan UI dengan cepat selama pengembangan
- Redux + React-Redux
- Semua kasus penggunaan untuk Redux + kemampuan komponen React untuk berinteraksi dengan penyimpanan Redux
Sekali lagi: alat bernama memecahkan masalah yang berbeda!
Rekomendasi
Jadi, bagaimana Anda memutuskan apa yang akan digunakan?
Untuk melakukan ini, Anda hanya perlu menentukan alat mana yang paling baik memecahkan masalah aplikasi Anda.
- Jika Anda hanya ingin menghindari "pengeboran", gunakan konteks
- , , + useReducer()
- , , .., Redux + React-Redux
Saya yakin jika aplikasi Anda memiliki 2-3 konteks untuk mengelola status, Anda harus beralih ke Redux.
Anda akan sering mendengar bahwa "menggunakan Redux melibatkan penulisan banyak kode boilerplate", namun "Redux modern" membuatnya lebih mudah dipelajari dan digunakan. Redux Toolkit resmi memecahkan masalah templating, dan hook React-Redux memudahkan penggunaan Redux di komponen React.
Tentu saja, menambahkan RTK dan React-Redux sebagai dependensi akan meningkatkan bundel aplikasi melalui konteks + useReducer (), yang sudah ada di dalamnya. Tetapi keuntungan dari pendekatan ini mencakup kerugian - pelacakan status yang lebih baik, logika yang lebih sederhana dan lebih dapat diprediksi, peningkatan optimalisasi rendering komponen.
Penting juga untuk dicatat bahwa yang satu tidak mengecualikan yang lain - Anda dapat menggunakan Redux, Context, dan useReducer () bersama-sama. Kami merekomendasikan untuk menyimpan status "global" di Redux dan status lokal di komponen, dan berhati-hatilah saat menentukan bagian aplikasi mana yang harus disimpan di Redux dan di komponen mana.... Jadi, Anda dapat menggunakan Redux untuk menyimpan status global, Context + useReducer () untuk menyimpan status lokal, dan Context untuk menyimpan nilai statis, semuanya secara bersamaan dalam aplikasi yang sama.
Sekali lagi, saya tidak memperdebatkan bahwa semua status aplikasi harus disimpan di Redux, atau bahwa Redux selalu merupakan solusi terbaik. Maksud saya adalah bahwa Redux adalah pilihan yang bagus, ada banyak alasan untuk menggunakan Redux, dan biaya penggunaannya tidak setinggi yang diperkirakan banyak orang.
Akhirnya, konteks dan Redux bukanlah satu-satunya. Ada banyak alat lain yang membahas aspek lain dari manajemen negara. MobX adalah solusi populer yang menggunakan OOP dan observable untuk memperbarui dependensi secara otomatis. Pendekatan lain untuk pembaruan negara termasuk Jotai, Recoil, dan Zustand. Pustaka data seperti React Query, SWR, Apollo, dan Urql menyediakan abstraksi yang memudahkan penggunaan pola umum untuk bekerja dengan status cache server (pustaka serupa ( RTK Query ) akan segera muncul untuk Redux Toolkit).
Saya harap artikel ini membantu Anda memahami perbedaan antara konteks dan Redux, dan alat mana yang harus digunakan dan kapan. Terima kasih atas perhatian Anda.