Redux - Tidak diperlukan! Ganti dengan useContext dan useReducer in React?

gambar


Selamat siang, Khabrovsk!


Saya ingin berbicara tentang bagaimana saya baru-baru ini belajar tentang "kait" tertentu di Bereaksi. Mereka muncul relatif baru-baru ini, dalam versi [16.8.0] pada 6 Februari 2019 (yang, menurut kecepatan pengembangan FrontEnd, sudah lama sekali)


Setelah membaca dokumentasi, saya fokus pada hook useReducer dan segera bertanya pada diri sendiri pertanyaan: "Hal ini sepenuhnya dapat menggantikan Redux!?" Saya menghabiskan beberapa malam di eksperimen dan sekarang saya ingin berbagi hasil dan kesimpulan saya.


Apakah saya perlu mengganti Redux dengan useContext + useReducer?


Untuk yang tidak sabar - segera kesimpulan


Untuk:


  • Anda dapat menggunakan pengait (useContext + useReducer) alih-alih Redux dalam aplikasi kecil (di mana tidak perlu Reduksi gabungan besar). Dalam hal ini, Redux mungkin memang berlebihan.

Terhadap:


  • Sejumlah besar kode telah ditulis pada sekelompok React + Redux dan sepertinya tidak disarankan untuk menulis ulang ke hooks (useContext + useReducer), setidaknya untuk saat ini.
  • Redux adalah perpustakaan yang terbukti, kait adalah sebuah inovasi, antarmuka dan perilaku mereka dapat berubah di masa depan.
  • Untuk membuat penggunaan useContext + useReducer benar-benar nyaman, Anda harus menulis beberapa sepeda.

Kesimpulannya adalah pendapat pribadi penulis dan tidak mengklaim sebagai kebenaran tanpa syarat - jika Anda tidak setuju, saya akan senang melihat kritik konstruktif Anda di komentar.


Mari kita coba mencari tahu


Mari kita mulai dengan contoh sederhana.


(reducer.js)


import React from "react"; export const ContextApp = React.createContext(); export const initialState = { app: { test: 'test_context' } }; export const testReducer = (state, action) => { switch(action.type) { case 'test_update': return { ...state, ...action.payload }; default: return state } }; 

Sejauh ini, peredam kami terlihat persis sama dengan di Redux


(app.js)


 import React, {useReducer} from 'react' import {ContextApp, initialState, testReducer} from "./reducer.js"; import {IndexComponent} from "./IndexComponent.js" export const App = () => { //  reducer   state + dispatch   const [state, dispatch] = useReducer(testReducer, initialState); return ( //  ,     reducer   //  ContextApp   (dispatch  state) //      <ContextApp.Provider value={{dispatch, state}}> <IndexComponent/> </ContextApp.Provider> ) }; 

(IndexComponent.js)


 import React, {useContext} from "react"; import {ContextApp} from "./reducer.js"; export function IndexComponent() { //   useContext    ContextApp //  IndexComponent      ContextApp.Provider const {state, dispatch} = useContext(ContextApp); return ( //  dispatch    reducer.js   testReducer //    .    Redux <div onClick={() => {dispatch({ type: 'test_update', payload: { newVar: 123 } })}}> {JSON.stringify(state)} </div> ) } 

Ini adalah contoh paling sederhana di mana kita sederhana pembaruan tulis data baru ke reducer datar (tanpa bersarang)
Secara teori, Anda bahkan dapat mencoba menulis seperti ini:


(reducer.js)


 ... export const testReducer = (state, data) => { return { ...state, ...data } ... 

(IndexComponent.js)


 ... return ( //      ,   type <div onClick={() => {dispatch({ newVar: 123 }> {JSON.stringify(state)} </div> ) ... 

Jika kami tidak memiliki aplikasi besar dan sederhana (yang jarang terjadi pada kenyataannya), maka Anda tidak dapat menggunakan jenis dan selalu mengelola pembaruan peredam langsung dari tindakan. Ngomong-ngomong, dengan mengorbankan pembaruan, dalam hal ini kami hanya menulis data baru dalam peredam, tetapi bagaimana jika kami harus mengubah satu nilai di pohon dengan beberapa tingkat sarang?


Lebih rumit sekarang


Mari kita lihat contoh berikut:


(IndexComponent.js)


 ... return ( //        //     -     //      ,     callback: <div onClick={() => { //  ,    callback, //   testReducer     state (state) => { const {tree_1} = state; return { tree_1: { ...tree_1, tree_2_1: { ...tree_1.tree_2_1, tree_3_1: 'tree_3_1 UPDATE' }, }, }; }> {JSON.stringify(state)} </div> ) ... 

(reducer.js)


 ... export const initialState = { tree_1: { tree_2_1: { tree_3_1: 'tree_3_1', tree_3_2: 'tree_3_2' }, tree_2_2: { tree_3_3: 'tree_3_3', tree_3_4: 'tree_3_4' } } }; export const testReducer = (state, callback) => { //      state      //      callback const action = callback(state); return { ...state, ...action } ... 

Oke, kami menemukan pembaruan pohon juga. Meskipun dalam hal ini sudah lebih baik untuk kembali menggunakan jenis di dalam testReducer dan memperbarui pohon sesuai dengan jenis tindakan tertentu. Semuanya seperti di Redux, hanya bundel yang dihasilkan sedikit lebih kecil [8].


Operasi dan pengiriman asinkron


Tapi apakah semuanya baik-baik saja? Apa yang terjadi jika kita menggunakan operasi asinkron?
Untuk melakukan ini, kita harus mendefinisikan pengiriman kita sendiri. Ayo kita coba!


(action.js)


 export const actions = { sendToServer: function ({dataForServer}) { //      ,   dispatch return function (dispatch) { //   dispatch    , //   state      dispatch(state => { return { pending: true } }); } } 

(IndexComponent.js)


 const [state, _dispatch] = useReducer(AppReducer, AppInitialState); //     dispatch   -> //    ,  Proxy const dispatch = (action) => action(_dispatch); ... dispatch(actions.sendToServer({dataForServer: 'data'})) ... 

Semuanya tampaknya baik-baik saja juga, tetapi sekarang kita memiliki banyak callback bersarang , yang tidak terlalu keren, jika kita hanya ingin mengubah keadaan tanpa membuat fungsi tindakan, kita harus menulis konstruksi semacam ini:


(IndexComponent.js)


 ... dispatch( (dispatch) => dispatch(state => { return { {dataForServer: 'data'} } }) ) ... 

Ternyata sesuatu yang menakutkan, bukan? Untuk pembaruan data sederhana, saya ingin menulis sesuatu seperti ini:


(IndexComponent.js)


 ... dispatch({dataForServer: 'data'}) ... 

Untuk melakukan ini, Anda harus mengubah proxy untuk fungsi pengiriman yang kami buat sebelumnya
(IndexComponent.js)


 const [state, _dispatch] = useReducer(AppReducer, AppInitialState); //  // const dispatch = (action) => action(_dispatch); //  const dispatch = (action) => { if (typeof action === "function") { action(_dispatch); } else { _dispatch(() => action) } }; ... 

Sekarang kita bisa melewati fungsi aksi dan objek sederhana untuk dikirim.
Tapi! Dengan transfer objek yang sederhana, Anda harus berhati-hati, Anda mungkin tergoda untuk melakukan ini:


(IndexComponent.js)


 ... dispatch({ tree: { //  state         AppContext ...state.tree, data: 'newData' } }) ... 

Mengapa contoh ini buruk? Dengan fakta bahwa pada saat pengiriman ini diproses, keadaan dapat diperbarui melalui pengiriman lain, tetapi perubahan ini belum mencapai komponen kami dan pada dasarnya kami menggunakan contoh keadaan lama yang akan menimpa semuanya dengan data lama.


Untuk alasan ini, metode seperti itu menjadi sangat tidak berguna, hanya untuk memperbarui reduksi datar di mana tidak ada sarang dan Anda tidak perlu menghubungi negara untuk memperbarui objek bersarang. Pada kenyataannya, reduksi jarang benar-benar datar, jadi saya menyarankan Anda untuk tidak menggunakan metode ini sama sekali dan hanya memperbarui data melalui tindakan.


(action.js)


 ... // ..  dispatch   callback,    //       (. reducer.js) dispatch(state => { return { dataFromServer: { ...state.dataFromServer, form_isPending: true } } }); axios({ method: 'post', url: `...`, data: {...} }).then(response => { dispatch(state => { //   axios     //         dispatch //     ,  state -    , // ..       testReducer (reducer.js) return { dataFromServer: { ...state.dataFromServer, form_isPending: false, form_request: response.data }, user: {} } }); }).catch(error => { dispatch(state => { // , state -    ) return { dataFromServer: { ...state.dataFromServer, form_isPending: false, form_request: { error: error.response.data } }, } }); ... 

Kesimpulan:


  • Itu adalah pengalaman yang menarik, saya memperkuat pengetahuan akademis saya dan belajar fitur baru dari reaksi
  • Saya tidak akan menggunakan pendekatan ini dalam produksi (setidaknya dalam enam bulan ke depan). Untuk alasan yang telah dijelaskan di atas (ini adalah fitur baru, dan Redux adalah alat yang terbukti dan dapat diandalkan) + Saya tidak memiliki masalah kinerja untuk mengejar milidetik yang dapat dimenangkan dengan meninggalkan editor [8]

Saya akan senang mengetahui, dalam komentar, pendapat rekan-rekan dari bagian depan Habrosobschestva kami!


Referensi:


Source: https://habr.com/ru/post/id473070/


All Articles