Organisasi peredam melalui kelas standar

Salam, hari ini saya akan berbicara dengan Anda tentang bagaimana Reducer diatur. Dan untuk mengetahui di mana saya mulai dan apa yang saya datangi.


Jadi, ada standar tertentu untuk mengatur Peredam dan terlihat seperti ini:


export default function someReducer(state = initialState, action) { switch (action.type) { case 'SOME_REDUCER_LABEL': return action.data || {}; default: return state; } } 

Semuanya sederhana dan jelas di sini, tetapi setelah bekerja sedikit dengan konstruksi seperti itu, saya menyadari bahwa metode ini memiliki sejumlah kesulitan.


  • Tag perlu disimpan entah bagaimana, karena mereka mulai merayap di sepanjang proyek dan merangkak jauh melampaui pengontrol.
  • Label harus dibuat unik, karena kalau tidak bisa ada persimpangan dengan reduksi lainnya
  • Sebagian besar waktu ketika bekerja dengan struktur seperti itu dihabiskan untuk mengatur kode daripada memproses data yang masuk
  • Dan ketika ada banyak label dalam peredam, kode menjadi ceroboh dan sulit dibaca, dan ruang nama umum tidak secara terbuka tolong saya.
    Sekitar waktu yang sama, kami mulai menggunakan kisah-kisah untuk memproses efek samping. Ini memungkinkan kami untuk sangat memudahkan komunikasi dengan sisi server tanpa menggunakan panggilan balik.

Sekarang kami harus memberi tahu saga peredam mana yang perlu dipanggil setelah efek samping bekerja.


Opsi paling masuk akal yang saya temukan adalah membuat pembuat tindakan.


Dan kode kami sebelumnya mulai terlihat seperti ini:


  import { FetchSaga } from '../../helpers/sagasHelpers'; const SOME_REDUCER_LABEL = 'SOME_REDUCER_LABEL'; export const someReducerLabelActionCreator = FetchSaga.bind(this, SOME_REDUCER_LABEL); export default function someReducer(state = initialState, action) { switch (action.type) { case SOME_REDUCER_LABEL: return action.data || {}; default: return state; } } 

FetchSaga adalah fungsi pembuat tindakan (selanjutnya disebut sebagai pembuat tindakan) untuk kisah yang meminta data dari server dan mengirimkannya ke peredam, label yang diteruskan ke fungsi pada tahap inisialisasi (SOME_REDUCER_LABEL).


Sekarang, label peredam diekspor dari peredam, atau pembuat tindakan diekspor dari peredam untuk saga dan yang standar. Selain itu, pawang seperti itu dibuat untuk setiap label. Ini hanya menambah sakit kepala, karena begitu saya membuka peredam, saya menghitung 10 konstanta dari label yang menentukan, kemudian beberapa panggilan untuk berbagai pembuat tindakan untuk kisah-kisah dan kemudian fungsi lain untuk memproses keadaan peredam, itu terlihat seperti ini


 import { FetchSaga } from '../../helpers/sagasHelpers'; const SOME_REDUCER_LABEL1 = 'SOME_REDUCER_LABEL1';  .... const SOME_REDUCER_LABEL10 = 'SOME_REDUCER_LABEL10'; export const someReducerLabelActionCreator1 = FetchSaga.bind(this, SOME_REDUCER_LABEL1);   ..... export const someReducerLabelActionCreator10 = FetchSaga.bind(this, SOME_REDUCER_LABEL10); export default function someReducer(state = initialState, action) { switch (action.type) { case SOME_REDUCER_LABEL: return action.data || {}; case SOME_REDUCER_LABEL1: return action.data || {}; case SOME_REDUCER_LABEL2: return action.data || {}; case SOME_REDUCER_LABEL3: return action.data || {}; .... default: return state; } } 

Ketika mengimpor semua tindakan ini ke controller, yang satu itu juga sangat meningkat. Dan itu menghalangi.


Setelah melihat begitu banyak reduksi, saya pikir kami sedang menulis banyak kode utilitas yang tidak pernah berubah. Selain itu, kami harus memastikan bahwa kami mengirim status kloning ke komponen.


Lalu saya punya ide untuk membakukan peredam. Tugas di depannya tidak sulit.


  1. Periksa tindakan yang masuk dan kembalikan keadaan lama jika tindakan itu bukan untuk peredam saat ini, atau secara otomatis klon keadaan dan meneruskannya ke metode handler, yang akan mengubah keadaan dan mengembalikannya ke komponen.
  2. Anda harus berhenti menggunakan label, sebagai gantinya, controller harus menerima objek yang berisi semua pembuat aksi untuk peredam yang kami minati.
    Jadi, setelah mengimpor set seperti itu sekali, saya dapat melewatinya sejumlah pembuat tindakan untuk mengirim fungsi dari reducer ke controller tanpa perlu mengimpor kembali
  3. alih-alih menggunakan switch-case yang kikuk dengan namespace umum yang digunakan oleh linter, saya ingin memiliki metode terpisah untuk setiap tindakan, di mana keadaan kloning peredam dan tindakan itu sendiri akan ditransfer
  4. alangkah baiknya bisa mewarisi peredam baru dari peredam. Dalam hal logika berulang, tetapi misalnya untuk serangkaian label yang berbeda.

Gagasan itu tampak masuk akal bagi saya dan saya memutuskan untuk mencoba menerapkannya.


Beginilah rata-rata peredam mulai terlihat seperti sekarang


  //    ,        reducer' import stdReducerClass from '../../../helpers/reducer_helpers/stdReducer'; class SomeReducer extends stdReducerClass { constructor() { super(); /**   reducer'.   reducer    action,     */ this.prefix = 'SOME_REDUCER__'; } /**   ,     reducer - type - ,    . -        ,     action creator,  SOME_REDUCE__FETCH.   type        action creator  someReduceInstActions - method - ,      action,            - sagas -    ,   ,       .    ,   action creator  ,      SOME_REDUCE__FETCH,  ,   ,      reducer    . */ config = () => [ { type: 'fetch', method: this.fetch, saga: 'fetch' }, { type: 'update', method: this.update }, ]; //           action creators init = () => this.subscribeReduceOnActions(this.config()); //  ,      fetch = (clone, action) => { //       return clone; }; //  ,         update = (clone, action) => { //       return clone; }; } const someReducerInst = new SomeReducer(); someReducerInst.init(); //   action creators   config //    action creator      export const someReducerInstActions = someReducerInst.getActionCreators(); //    .   checkActionForState    Action  ,      reducer'   export default someReducerInst.checkActionForState; 

stdReducerClass dari dalam adalah sebagai berikut


 import { cloneDeep } from 'lodash'; //    lodash //        ,            import { FetchSaga } from '../helpers/sagasHelpers/actions'; export default class StdReducer { _actions = {}; actionCreators = {}; /** UNIQUE PREFIX BLOCK START */ /**      ,  .   ,   ,    ,  reducer    action       */ uniquePrefix = ''; set prefix(value) { const lowedValue = value ? value.toLowerCase() : ''; this.uniquePrefix = lowedValue; } get prefix() { return this.uniquePrefix; } /** INITIAL STATE BLOCK START */ /**   initialState      reducer'. */ initialStateValues = {}; set initialState(value) { this.initialStateValues = value; } get initialState() { return this.initialStateValues; } /** PUBLIC BLOCK START */ /** *        init() .   ,      Config, action creator   _subscribeAction * actionsConfig -     ,     {type, method, saga?}     ,     action creator          */ subscribeReducerOnActions = actionsConfig => actionsConfig.forEach(this._subscribeAction); /**      _subscribeAction,    ,        type.  , reducer  ,       action. */ _subscribeAction = (action) => { const type = action.type.toLowerCase(); this._actions[type] = action.method; //       this.actionCreators[type] = this._subscribeActionCreator(type, action.saga); //   action creator   } /** _subscribeActionCreator -   , action creator          -   saga    ,      -   fetch           ,             type    ,   ,    action creator,  ,     SOME_Reducer__,      FETCH,      SOME_Reducer__FETCH,     action creator */ _subscribeActionCreator = (type, creatorType) => { const label = (this.prefix + type).toUpperCase(); switch (creatorType) { case 'fetch': return this._getFetchSaga(label); default: return this._getActionCreator(label); } } /** _getFetchSaga -     ,          */ _getFetchSaga = label => FetchSaga.bind(this, label); /** _getActionCreator -  action creator,      ,   ,    . */ _getActionCreator = label => (params = {}) => ({ type: label, ...params }); /**    ,      playload.     action   ,    */ checkActionForState = (state = this.initialState || {}, action) => { if (!action.type) return state; const type = action.type.toLowerCase(); const prefix = this.prefix;       ,    ,   . const internalType = type.replace(prefix, ''); //        if (this._actions[internalType]) { //     -    const clone = cloneDeep(state); //  ,    ,  action  ,     //        return this._actions[internalType](clone, action); } //   ,   action   .     return state; } /**       action creator,    reducer */ getActionCreators = () => this.actionCreators; } 

Bagaimana tampilannya di controller? Dan begitulah


 import { someReducerInstActions } from '../../../SomeReducer.js' const mapDispatchToProps = dispatch => ({ doSoAction: (params) => dispatch(someReducerInstActions.fetch(url, params)), doSoAction1: (value, block) => dispatch(someReducerInstActions.update({value, block})), }); 

Jadi, apa yang kita miliki sebagai hasilnya:


  1. menyingkirkan menumpuk tag
  2. menyingkirkan banyak impor di controller
  3. switch-case yang dihapus
  4. mereka mengalahkan saga sekali dan sekarang kita dapat memperluas set mereka di satu tempat, memastikan bahwa semua ahli waris akan secara otomatis menerima penangan efek samping tambahan
  5. Kami mendapat kesempatan untuk mewarisi dari reduksi, jika ada logika terkait (saat ini tidak pernah berguna bagi saya =))
  6. Mereka mengalihkan tanggung jawab untuk mengkloning dari pengembang ke kelas yang pasti akan ingat untuk melakukannya.
  7. kurang rutin saat membuat peredam
  8. Setiap metode memiliki namespace yang terisolasi

Saya mencoba menggambarkan semuanya sedetail mungkin =) Maaf, jika bingung, Chukchi bukan penulis. Saya berharap pengalaman saya akan bermanfaat bagi seseorang.


Contoh saat ini dapat dilihat di sini


Terima kasih sudah membaca!


UPD: memperbaiki kesalahan. Dia menulis di malam hari, membacanya dengan buruk. Terima kasih telah menunjukkannya dengan sangat hati-hati =)

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


All Articles