Redux: Perhatian pada combReducer, ketika suatu tindakan terkait dengan beberapa reduksi

Dibuat pada 21 Agu 2015  ·  52Komentar  ·  Sumber: reduxjs/redux

Saya sedang mengerjakan aplikasi obrolan yang dibuat dengan React.js dan Flux. Saat saya membaca dokumen Redux, fungsi combineReducer tampak aneh bagi saya. Sebagai contoh:

Ada tiga toko bernama messageStore , unreadStore , threadStore . Dan ada dan tindakan yang disebut NEW_MESSAGE . Ketiga toko akan memperbarui pesan baru. Di Redux, toko adalah pengurang yang digabungkan dengan combineReducer seperti:

message = (state=[], action) ->
  switch action.type
    when NEW_MESSAGE
      state # new state
unread = (state=[], action) ->
  switch action.type
    when NEW_MESSAGE
      state # new state
thread = (state=[], action) ->
  switch action.type
    when NEW_MESSAGE
      state # new state

combineReducer {message, unread, thread}

Bagi saya, dalam kasus seperti itu, memisahkan reduksi tidak membuat hidup saya lebih mudah. Namun toko raksasa yang dibangun dengan immutable-js mungkin merupakan solusi yang lebih baik untuk ini. Suka:

initialState = Immutable.fromJS
  message: []
  unread: []
  thread: []

allStore = (store=initialState, action) ->
  switch action.type
    when NEW_MESSAGE
        initialState
        .updateIn ['message'], (messages) -> messages # updated
        .updateIn ['urnead'], (urneads) -> unreads # updated
        .updateIn ['thread'], (threads) -> threads # updated
question

Komentar yang paling membantu

untuk tindakan seperti itu, saya harus menyimpannya di beberapa reduksi (atau file setelah aplikasi saya tumbuh). Dalam basis kode kami saat ini, kami telah melihat 5 toko menangani tindakan yang sama, agak sulit untuk mempertahankannya.

Baik atau buruk tergantung pada aplikasi Anda. Dalam pengalaman saya, memiliki banyak toko (atau reduksi Redux) yang menangani tindakan yang sama sebenarnya sangat nyaman karena membagi tanggung jawab, dan juga memungkinkan orang bekerja di cabang fitur tanpa bertabrakan dengan anggota tim lainnya. Dalam pengalaman saya, lebih mudah mempertahankan mutasi yang tidak terkait secara terpisah daripada satu mutasi raksasa.

Tapi Anda benar, ada kasus di mana ini tidak berfungsi dengan baik. Saya akan mengatakan mereka sering merupakan gejala model keadaan suboptimal. Sebagai contoh,

dalam beberapa kasus, satu peredam mungkin bergantung pada data dari yang lain (seperti memindahkan pesan dari belum dibaca ke pesan, atau bahkan mempromosikan pesan sebagai utas baru dari pesan ke utas)

adalah gejala dari suatu masalah. Jika Anda harus sering memindahkan barang ke dalam keadaan Anda, mungkin bentuk keadaan perlu lebih dinormalisasi. Alih-alih { readMessages, unreadMessages } Anda dapat memiliki { messagesById, threadsById, messageIdsByThreadIds } . Pesan sudah dibaca? Baik, alihkan state.messagesById[id].isRead . Ingin membaca pesan utas? Dapatkan state.threadsById[threadId].messageIds , lalu messageIds.map(id => state.messagesById[id]) .

Saat data dinormalisasi, Anda tidak perlu menyalin item dari satu array ke array lain. Sering kali Anda akan membalik bendera atau menambahkan / menghapus / mengaitkan / memisahkan ID.

Semua 52 komentar

Dapatkah Anda menjelaskan mengapa menurut Anda combineReducers tidak membantu dalam kasus ini? Memang, untuk status mirip database, Anda sering menginginkan peredam tunggal (dan kemudian peredam lain untuk status UI, misalnya, item yang dipilih, dll).

Lihat juga:

https://github.com/rackt/redux/tree/master/examples/real-world/reducers
https://github.com/rackt/redux/blob/master/examples/async/reducers

untuk inspirasi.

Contoh pertama yang Anda tunjuk mirip dengan kasus saya, requestType ditangani di kedua reduksi.
https://github.com/rackt/redux/blob/master/examples/real-world/reducers/paginate.js#L28

Pereduksi pemisah memudahkan perawatan saat dua peredam dipisahkan dari earch lainnya. Tetapi ketika mereka melakukan tindakan yang sama, itu mengarah pada:

  • untuk tindakan seperti itu, saya harus menyimpannya di beberapa reduksi (atau file setelah aplikasi saya tumbuh). Dalam basis kode kami saat ini, kami telah melihat 5 toko menangani tindakan yang sama, agak sulit untuk mempertahankannya.
  • dalam beberapa kasus, satu peredam mungkin bergantung pada data dari yang lain (seperti memindahkan pesan dari unread ke message , atau bahkan mempromosikan pesan sebagai utas baru dari message menjadi thread ). Jadi saya mungkin perlu mendapatkan data dari peredam lain sekarang.

Saya tidak melihat solusi yang bagus untuk kedua masalah ini. Sebenarnya saya tidak begitu yakin tentang kedua masalah ini, mereka seperti pertukaran.

untuk tindakan seperti itu, saya harus menyimpannya di beberapa reduksi (atau file setelah aplikasi saya tumbuh). Dalam basis kode kami saat ini, kami telah melihat 5 toko menangani tindakan yang sama, agak sulit untuk mempertahankannya.

Baik atau buruk tergantung pada aplikasi Anda. Dalam pengalaman saya, memiliki banyak toko (atau reduksi Redux) yang menangani tindakan yang sama sebenarnya sangat nyaman karena membagi tanggung jawab, dan juga memungkinkan orang bekerja di cabang fitur tanpa bertabrakan dengan anggota tim lainnya. Dalam pengalaman saya, lebih mudah mempertahankan mutasi yang tidak terkait secara terpisah daripada satu mutasi raksasa.

Tapi Anda benar, ada kasus di mana ini tidak berfungsi dengan baik. Saya akan mengatakan mereka sering merupakan gejala model keadaan suboptimal. Sebagai contoh,

dalam beberapa kasus, satu peredam mungkin bergantung pada data dari yang lain (seperti memindahkan pesan dari belum dibaca ke pesan, atau bahkan mempromosikan pesan sebagai utas baru dari pesan ke utas)

adalah gejala dari suatu masalah. Jika Anda harus sering memindahkan barang ke dalam keadaan Anda, mungkin bentuk keadaan perlu lebih dinormalisasi. Alih-alih { readMessages, unreadMessages } Anda dapat memiliki { messagesById, threadsById, messageIdsByThreadIds } . Pesan sudah dibaca? Baik, alihkan state.messagesById[id].isRead . Ingin membaca pesan utas? Dapatkan state.threadsById[threadId].messageIds , lalu messageIds.map(id => state.messagesById[id]) .

Saat data dinormalisasi, Anda tidak perlu menyalin item dari satu array ke array lain. Sering kali Anda akan membalik bendera atau menambahkan / menghapus / mengaitkan / memisahkan ID.

Saya ingin mencobanya.

Saya baru saja menulis halaman demo untuk mencoba redux. Saya pikir combineReducer membawa cukup banyak kerumitan bagi saya. Dan semua contoh di repo menggunakan combineReducer , apakah masih ada kemungkinan saya dapat menggunakan satu objek data yang tidak dapat diubah sebagai model, daripada objek JavaScript yang berisi data saya?

@jiyinyiyong Saya benar-benar tidak yakin kerumitan apa yang Anda bicarakan, tetapi jangan ragu untuk tidak menggunakan combineReducers . Itu hanya pembantu. Lihat helper serupa yang menggunakan Immutable sebagai gantinya . Atau tulis sendiri tentunya.

Saya menulis combineReducers() helper saya sendiri sebagian besar untuk mendukung Immutable.js, tetapi diperlukan reduksi tambahan yang diperlakukan seperti pengurang root:
https://github.com/jokeyrhyme/wow-healer-bootcamp/blob/master/utils/combineReducers.js

Argumen pertama cocok dengan bentuk negara bagian Anda dan meneruskan sub-status tingkat atas ke sub-pereduksi yang cocok yang Anda berikan. Ini kurang lebih sama dengan versi resminya.

Namun, adalah opsional untuk menambahkan nol atau lebih argumen tambahan, ini diperlakukan seperti pereduksi root lainnya. Pengurang ini melewati keadaan lengkap. Hal ini memungkinkan tindakan untuk dikirim dengan cara yang memerlukan lebih banyak akses daripada pola sub-peredam yang direkomendasikan.

Jelas bukan ide bagus untuk menyalahgunakan ini, tetapi saya telah menemukan kasus aneh di mana menyenangkan memiliki banyak sub-reduksi serta beberapa pereduksi root.

Mungkin alih-alih menambahkan beberapa pereduksi akar, langkah tengah dapat menggunakan argumen tambahan untuk menentukan sub-pereduksi yang membutuhkan akses lebih luas (masih kurang dari akses total). Sebagai contoh:

function a (state, action) { /* only sees within a */ return state; }
function b (state, action) { /* only sees within b */ return state; }
function c (state, action) { /* only sees within c */ return state; }

function ab (state, action) { /* partial state containing a and b */ return state; }
function bc (state, action) { /* partial state containing b and c */ return state; }

let rootReducer = combineReducers(
  { a, b, c },
  [ 'a', 'b', ab ],
  [ 'b', 'c', bc ]
);

Apakah layak mengirimkan PR untuk pengurang parsial dan pengurang akar tambahan? Apakah ini ide yang buruk, atau apakah ini sesuatu yang dapat berguna bagi cukup banyak orang?

Saya lebih ingin mempelajari bagaimana halogen dan elm menangani masalah ini. JavaScript adalah campuran dari beberapa diagram program dan membawa kita ke banyak cara. Saya sekarang bingung mana yang benar dari FRP dan aliran data searah.

Saya telah menemukan ini menjadi titik kebingungan bagi saya juga. Cukup posting kasus penggunaan / solusi saya di sini.

Saya memiliki tiga subreduser yang menangani serangkaian tiga jenis sumber daya yang berbeda (klien, proyek, pekerjaan). Ketika saya menghapus klien ( REMOVE_CLIENT ), semua proyek dan pekerjaan terkait harus dihapus. Pekerjaan terkait dengan klien melalui sebuah proyek, jadi peredam pekerjaan harus memiliki akses ke proyek untuk melakukan logika penggabungan. Peredam pekerjaan harus mendapatkan daftar semua ID proyek milik klien yang sedang dihapus, lalu menghapus pekerjaan yang cocok dari toko.

Saya mengatasi masalah ini dengan middleware berikut:

export default store => next => action => {
  next({ ...action, getState: store.getState });
}

Sehingga setiap tindakan memiliki akses ke penyimpanan root melalui action.getState() .

@ rhys-vdw terima kasih untuk itu! Middleware yang sangat membantu :)

@ rhys-vdw Terima kasih untuk ini - sepertinya solusi yang bagus. Bagaimana Anda menangani kasus tepi / integritas referensial, misalnya ketika sebuah entitas dibagikan oleh dua (atau lebih) entitas lain atau menunjuk ke rekaman yang tidak ada selama penghapusan. Hanya ingin mendengar pendapat Anda tentang ini.
@ Gaearon Apakah ada cara terdokumentasi di Redux untuk memecahkan masalah integritas referensial semacam ini dari entitas yang dinormalisasi?

Tidak ada cara khusus. Saya akan membayangkan bahwa seseorang dapat mengotomatiskan ini dengan mendefinisikan skema dan menghasilkan reduksi berdasarkan skema ini. Mereka kemudian akan “tahu” bagaimana mengumpulkan “sampah” ketika ada sesuatu yang dihapus. Namun ini semua sangat bergelombang dan membutuhkan upaya implementasi :-). Secara pribadi saya tidak berpikir penghapusan terjadi cukup sering untuk menjamin pembersihan yang nyata. Saya biasanya akan membalik bidang status pada model dan memastikan untuk tidak menampilkan item yang dihapus saat memilihnya. Atau, tentu saja, Anda dapat menyegarkan saat menghapus jika ini bukan operasi yang umum.

@gaearon Cheers untuk balasan super cepat. Ya… Saya pikir seseorang memiliki upaya yang sama dalam menangani integritas referensial saat menggunakan referensi dengan DB berbasis dokumen seperti Mongo. Saya pikir itu sangat berharga baik untuk memiliki keadaan murni tanpa entitas sampah mengambang sekitar. Tidak yakin apakah entri hantu itu bisa membuat Anda tersandung terutama ketika Anda memiliki banyak operasi CRUD di negara bagian Anda.

Namun saya juga berpikir itu akan menjadi ace jika Redux memiliki dukungan untuk keduanya - entitas yang dinormalisasi dan tertanam. Dan seseorang dapat memilih strategi yang sesuai untuk tujuan. Tapi saya kira untuk entitas yang disematkan harus menggunakan nested reducers yang merupakan anti-pola menurut dokumen Redux.

Saya merasa sulit untuk mengakses status penyimpanan lainnya di peredam. Itu membuat combingReducers bersarang sangat sulit untuk digunakan dalam beberapa kasus. Akan lebih baik jika ada metode untuk mengakses bagian lain dari status seperti .parent() atau .root() atau yang setara.

Mengakses status peredam lain di peredam paling sering merupakan anti-pola.
Ini biasanya dapat diselesaikan dengan:

  • Menghapus logika itu dari peredam dan memindahkannya ke pemilih;
  • Meneruskan informasi tambahan ke dalam tindakan;
  • Membiarkan kode tampilan melakukan dua tindakan.

Terima kasih! Saya setuju dengan itu. Saya baru saja menemukan itu menjadi lebih kompleks setelah mencoba meneruskan status root ke reduksi :-)

Ya, masalah dengan meneruskan status akar adalah bahwa reduksi menjadi digabungkan ke bentuk status satu sama lain yang mempersulit refactoring atau perubahan dalam struktur status.

Oke, saya melihat dengan jelas manfaat memiliki status yang dinormalisasi dan memperlakukan bagian redux dari status saya seperti DB.

Saya masih berpikir jika pendekatan @gaearon (menandai entitas untuk dihapus) atau pendekatan @ rhys-vdw lebih baik (menyelesaikan referensi dan menghapus referensi terkait di tempat) .

Ada ide / pengalaman dunia nyata?

Jika redux lebih seperti elm dan keadaan tidak dinormalisasi maka decoupling reduksi dari bentuk negara masuk akal. Namun karena statusnya dinormalisasi, tampaknya pereduksi sering kali memerlukan akses ke bagian lain dari status aplikasi.

Pereduksi sudah digabungkan secara transitif ke aplikasi karena pereduksi bergantung pada tindakan. Anda tidak dapat menggunakan kembali reduksi dengan mudah di aplikasi redux lain.

Mengatasi masalah dengan mendorong akses status ke dalam tindakan dan menggunakan middleware tampaknya merupakan bentuk penggandengan yang jauh lebih jelek, lalu mengizinkan pereduksi untuk mengakses status root secara langsung. Sekarang tindakan yang sebaliknya tidak memerlukannya digabungkan untuk menyatakan bentuk juga dan ada lebih banyak tindakan daripada reduksi dan lebih banyak tempat untuk diubah.

Meneruskan status ke dalam tampilan ke tindakan mungkin lebih baik mengisolasi dalam perubahan bentuk status, tetapi jika status tambahan diperlukan karena fitur ditambahkan sama seperti banyak tindakan yang perlu diperbarui.

IMHO mengizinkan akses pereduksi ke status root akan menghasilkan kopling keseluruhan yang lebih sedikit daripada solusi saat ini.

@kurtharđ : perhatikan bahwa meskipun pola umum yang disarankan adalah menyusun status Redux berdasarkan domain, dan menggunakan combineReducers untuk mengelola setiap domain secara terpisah, itu adalah _just_ rekomendasi. Sangat mungkin untuk menulis peredam tingkat atas Anda sendiri yang mengelola berbagai hal secara berbeda, termasuk meneruskan seluruh status ke fungsi sub-peredam tertentu. Pada akhirnya, Anda benar-benar hanya memiliki fungsi _one _ reducer, dan cara Anda memecahnya secara internal sepenuhnya terserah Anda. combineReducers hanyalah utilitas yang berguna untuk kasus penggunaan umum.

Jawaban FAQ Redux tentang pemisahan logika bisnis juga memiliki beberapa diskusi bagus tentang topik ini.

Ya, opsi lain adalah dengan hanya menggunakan satu peredam dalam satu fungsi raksasa dan / atau menulis fungsi gabunganReducer Anda sendiri daripada menggunakan fungsi bawaan.
Memiliki metode gabunganReducers bawaan yang menambahkan argumen status root tambahan dapat berguna, tetapi PR yang mencoba menambahkan fungsionalitas ini telah ditutup sebagai anti-pola. Saya hanya ingin memperdebatkan titik alternatif yang diusulkan IMHO menghasilkan kopling keseluruhan yang lebih banyak dan mungkin ini tidak boleh dianggap sebagai anti-pola. Juga jika status root ditambahkan sebagai argumen tambahan, itu tidak seperti Anda dipaksa untuk menggunakan sehingga penggandengan masih merupakan pilihan.

Kami ingin penggabungan ini menjadi eksplisit. Ini eksplisit ketika Anda melakukannya secara manual dan secara harfiah N baris kode di mana N adalah berapa banyak reduksi yang Anda miliki. Hanya saja, jangan gunakan CombineReducers dan tulis peredam induk itu dengan tangan.

Saya baru mengenal redux / bereaksi tetapi dengan rendah hati akan menanyakan hal ini: jika pengurang tersusun yang mengakses status satu sama lain adalah anti-pola karena menambahkan kopling di antara area berbeda dari bentuk status, saya berpendapat bahwa penggandengan ini sudah terjadi di mapStateToProps ( ) dari connect (), karena fungsi tersebut menyediakan status root dan komponen menarik dari mana saja / mana saja untuk mendapatkan properti mereka.

Jika subpohon status akan dienkapsulasi, komponen harus memiliki status akses melalui pengakses yang menyembunyikan subpohon tersebut, sehingga bentuk status internal untuk cakupan tersebut dapat dikelola tanpa pemfaktoran ulang.

Saat saya mempelajari redux, saya berjuang dengan apa yang saya bayangkan sebagai masalah jangka panjang dengan aplikasi saya, yang merupakan penggabungan erat bentuk status di seluruh aplikasi - bukan oleh reduksi tetapi oleh mapStateToProps - seperti bilah sisi komentar yang harus mengetahui nama pengguna auth (masing-masing dikurangi oleh peredam komentar vs peredam auth, misalnya.) Saya sedang bereksperimen dengan membungkus semua akses negara dalam metode pengakses yang melakukan enkapsulasi semacam ini, bahkan di mapStateToProps, tetapi merasa seperti saya mungkin salah pohon.

@jwhiting : itulah salah satu dari beberapa tujuan menggunakan "fungsi selector" untuk mengekstrak data yang Anda butuhkan dari status, terutama di mapStateToProps . Anda dapat menyembunyikan status bentuk Anda sehingga hanya ada sedikit tempat yang benar-benar perlu mengetahui letak state.some.nested.field . Jika Anda belum melihatnya, lihat pustaka utilitas Reselect , dan bagian Computing Derived Data di dokumen Redux.

Ya. Anda juga tidak harus menggunakan Pilih Ulang jika volume data Anda kecil tetapi masih menggunakan pemilih (tulisan tangan). Lihat contoh shopping-cart untuk pendekatan ini.

@markerikson @gaearon itu masuk akal dan saya akan menjelajahinya, terima kasih. Menggunakan penyeleksi di mapStateToProps untuk apa pun di luar perhatian utama komponen itu (atau mungkin untuk semua akses negara) akan melindungi komponen dari perubahan dalam bentuk negara asing. Saya ingin menemukan cara untuk membuat enkapsulasi negara lebih ketat diberlakukan dalam proyek saya, sehingga disiplin kode bukan satu-satunya penjaganya dan menerima saran di sana.

Menyimpan semua kode dalam satu peredam bukanlah pemisahan yang baik
kekhawatiran. Jika peredam Anda dipanggil oleh root, Anda dapat memanggilnya secara manual
dan secara eksplisit berikan status root seperti yang saya pikir Anda sarankan, tetapi jika Anda
hierarki peredam induk berisi combinReducers yang harus Anda rip
di luar. Jadi mengapa tidak membuatnya lebih mudah dibuat sehingga Anda tidak perlu merobeknya
keluar nanti atau menggunakan thunks seperti yang kebanyakan orang lakukan?
Pada hari Sabtu, 16 April 2016 jam 20:27 Josh Whiting [email protected]
menulis:

@markerikson https://github.com/markerikson @gaearon
https://github.com/gaearon itu masuk akal dan saya akan menjelajahinya,
Terima kasih. Menggunakan penyeleksi di mapStateToProps untuk apa pun di luar itu
perhatian utama komponen (atau mungkin untuk semua akses negara) akan melindungi
komponen dari perubahan bentuk negara asing. Saya ingin mencari cara
untuk membuat enkapsulasi negara lebih ketat diberlakukan dalam proyek saya,
sehingga kode disiplin bukan satu-satunya penjaga untuk itu dan diterima
saran di sana.

-
Anda menerima ini karena Anda disebutkan.
Balas email ini secara langsung atau lihat di GitHub
https://github.com/reactjs/redux/issues/601#issuecomment -210940305

@kurtharđ : kita semua setuju bahwa menyimpan setiap bit terakhir dari logika reducer dalam satu fungsi adalah ide yang buruk, dan pekerjaan tersebut harus didelegasikan ke sub-fungsi dengan cara tertentu. Namun, setiap orang akan memiliki ide yang berbeda tentang bagaimana melakukan itu, dan kebutuhan yang berbeda untuk aplikasi mereka sendiri. Redux hanya menyediakan combineReducers sebagai utilitas bawaan untuk kasus penggunaan umum. Jika Anda tidak ingin menggunakan utilitas itu seperti yang ada saat ini, Anda tidak harus - Anda sepenuhnya dipersilakan untuk menyusun reduksi Anda dengan cara Anda sendiri, apakah itu menulis semuanya dengan tangan, atau menulis variasi Anda sendiri combineReducers yang melakukan berbagai hal dengan cara yang lebih sesuai dengan kebutuhan Anda.

Karena kita berbicara tentang pola dan pemisahan masalah, saya sebenarnya mengalami sesuatu yang terkait, tetapi dengan tindakan.

Yakni, pola ajax shouldFetch yang saya lihat di berbagai tempat tampaknya merupakan hubungan yang erat dengan bentuk status. Contohnya disini . Dalam contoh itu, bagaimana jika saya menggunakan kembali peredam tetapi tidak memiliki objek entitas di akar status? Tindakan itu kemudian akan gagal. Apa rekomendasinya disini? Haruskah saya menambahkan tindakan khusus kasus? Atau tindakan yang menerima pemilih?

@shadowii Memiliki seorang pembuat tindakan yang mengimpor pemilih tampaknya baik-baik saja bagi saya. Saya akan mengumpulkan pemilih dengan reduksi sehingga pengetahuan tentang bentuk negara dilokalkan dalam file tersebut.

Apa masalah jika pereduksi berbeda menangani jenis tindakan yang sama?
Misalnya, jika saya memiliki account dan auth reducer, keduanya menangani tipe tindakan LOGIN_SUCCESS (dengan token autentikasi dan data akun dalam payload), masing-masing satu hanya memperbarui bagian negara yang mereka kelola. Saya menemukan diri saya menggunakan pendekatan ini beberapa kali ketika suatu tindakan memengaruhi berbagai bagian negara bagian.

Apa masalah jika pereduksi berbeda menangani jenis tindakan yang sama?

Tidak ada masalah, itu pola yang sangat umum dan berguna. Sebenarnya itu adalah tindakan _reason_: jika tindakan dipetakan ke pengurang 1: 1, tidak ada gunanya memiliki tindakan sama sekali.

@ rhys-vdw saya mencoba menggunakan middleware yang Anda posting.

customMiddleare.js

const customMiddleware =  store => next => action => {
  next({ ...action, getState: store.getState });
};

dan dalam kreasi toko saya, saya punya

import customMiddleware from './customMiddleware';

var store = createStore(
        rootReducer,
        initialState,
        applyMiddleware(
            reduxPromise,
            thunkMiddleware,
            loggerMiddleware,
            customMiddleware
        )
    );
return store;

Saya mendapatkan Error berikut:

applyMiddleware.js? ee15: 49 Tidak Tertangkap TypeError: middleware bukan fungsi (…)
(fungsi anonim) @ applyMiddleware.js? ee15: 49
(fungsi anonim) @ applyMiddleware.js? ee15: 48
createStore @ createStore.js? fe4c: 65
configureStore @ configureStore.js? ffde: 45
(fungsi anonim) @ index.jsx? fdd7: 25
(fungsi anonim) @ bundle.js: 1639
webpack_require @ bundle.js: 556
fn @ bundle.js: 87 (fungsi anonim) @ bundle.js: 588
webpack_require @ bundle.js: 556
(fungsi anonim) @ bundle.js: 579
(fungsi anonim) @ bundle.js: 582

@ mars76 : Kemungkinan besar Anda tidak mengimpor sesuatu dengan benar. Jika itu benar-benar seluruh konten customMiddleware.js , maka Anda tidak mengekspor _anything_. Secara umum, Anda melewatkan empat middlewares ke applyMiddleware , dan mungkin salah satunya bukan referensi yang valid. Mundur, hapus semuanya, tambahkan kembali satu per satu, lihat mana yang tidak valid, dan cari tahu alasannya. Periksa pernyataan impor dan ekspor, periksa kembali bagaimana Anda seharusnya menginisialisasi sesuatu, dll.

Hai,

Terima kasih @markerikson

Saya mengubah sintaks seperti di bawah ini dan sekarang baik-baik saja:

customMiddleware.js

export function customMiddleware(store) { return function (next) { return function (action) { next(Object.assign({}, action, { getState: store.getState })); }; }; } console.log(customMiddleware);

Bagaimana pun saya mencoba mengakses toko di Action Creator itu sendiri (Bukan di Reducer). Saya perlu melakukan panggilan Async dan harus melewati berbagai bagian status toko yang dikelola oleh berbagai pereduksi.

Inilah yang saya lakukan. Tidak yakin apakah ini pendekatan yang tepat.

Saya mengirimkan tindakan dengan data dan di dalam peredam, saya sekarang memiliki akses ke seluruh Negara Bagian dan saya mengekstrak bagian yang diperlukan yang saya perlukan dan membuat Objek dan mengirimkan tindakan lain dengan data ini yang akan tersedia di Pembuat Tindakan saya di mana saya Saya membuat panggilan Asyn menggunakan data itu.

Kekhawatiran terbesar saya adalah Pengurang sekarang menyebut metode pembuat tindakan.

Hargai setiap komentar.

Terima kasih

@ mars76 : sekali lagi, Anda harus _tidak pernah _ mencoba mengirim dari dalam Peredam.

Untuk mengakses toko di dalam pembuat tindakan Anda, Anda dapat menggunakan middleware redux-thunk . Anda mungkin juga ingin membaca pertanyaan FAQ tentang berbagi data antar reduksi , serta inti yang saya tulis dengan beberapa contoh penggunaan umum .

Terima kasih banyak @markerikson.

Saya buruk Saya tidak menyadari tentang getState () di redux-thunk. Itu membuat pekerjaan saya lebih mudah dan membiarkan pengurang saya murni.

@gaearon ketika Anda menyebutkan meminta pembuat tindakan mengimpor pemilih , apakah ada dalam benak Anda bahwa seluruh status akan diteruskan ke pembuat tindakan untuk diserahkan ke pemilih, atau apakah saya melewatkan sesuatu?

@tacomanator : FYI, Dan biasanya tidak menanggapi ping acak dalam masalah Redux hari ini. Terlalu banyak prioritas lainnya.

Sering kali, jika Anda menggunakan pemilih di pembuat tindakan, Anda melakukannya dalam sekejap. Thunks memiliki akses ke getState , jadi seluruh pohon status dapat diakses:

import {someSelector} from "./selectors";

function someThunk(someParameter) {
    return (dispatch, getState) => {
        const specificData = someSelector(getState(), someParameter);

        dispatch({type : "SOME_ACTION", payload : specificData});    
    }
}

@markerikson terima kasih, itu masuk akal. FYI, alasan saya bertanya sebagian besar tentang penggunaan data yang diturunkan oleh penyeleksi untuk validasi logika bisnis daripada mengirimkannya ke peredam, tetapi itu juga memberi saya bahan untuk dipikirkan.

Peretasan cepat yang saya gunakan untuk menggabungkan reduksi menjadi satu adalah

const reducers = [ reducerA, reducerB, ..., reducerZ ];

let reducer = ( state = initialState, action ) => {
    return reducers.reduce( ( state, reducerFn ) => (
        reducerFn( state, action )
    ), state );
};

di mana reducerA sampai reducerZ adalah fungsi peredam individu.

@brianpkelley : ya, itu pada dasarnya https://github.com/acdlite/reduce-reducers .

@markerikson Ah bagus, pertama kali menggunakan react / redux / etc dan menemukan utas ini saat melihat masalah ini. Dilewati sampai akhir sekitar 1/2 jalan melalui lol

Heh, tentu.

FYI, Anda mungkin ingin membaca FAQ: "Apakah saya harus menggunakan combineReducers ?" dan bagian Structuring Reducers di dokumen. Juga, seri tutorial "Redux Praktis" saya memiliki beberapa posting yang mendemonstrasikan beberapa teknik peredam tingkat lanjut (lihat Bagian 7 dan Bagian 8).

Dua sen saya saat menggunakan redux-logic adalah untuk menambah tindakan dari status root di fungsi transform .

Contoh:

const formInitLogic = createLogic({
  type: 'FORM_INIT',
  transform({ getState, action }, next) {
    const state = getState();
    next({
      ...action,
      payload: {
        ...action.payload,
        property1: state.branch1.someProperty > 5,
        property2: state.branch1.anotherProperty + state.branch2.yetAnotherProperty,
      },
    });
  },
});

saya pikir ini lebih tentang struktur kode:
"jangan mengakses tindakan substate secara langsung"
setelah menggunakan redux di banyak proyek saya menemukan bahwa struktur kode terbaik adalah untuk menangani setiap sub negara sebagai komponen yang benar-benar terpisah setiap komponen ini memiliki folder dan logika sendiri, untuk mengimplementasikan ide ini saya menggunakan struktur file berikut:

  • root redux

    • ekspor reducers.js menggabungkan reduksi dari setiap negara bagian

    • middleware.js ekspor applyMiddleware untuk semua middleware

    • configureStore.js createStore menggunakan (reducers.js, middleware.js)

    • actions.js mengekspor tindakan yang mengirimkan tindakan dari folder negara <== INI

    • state1

    • initial.js file ini berisi status awal (saya menggunakan immutable untuk membuat rekaman)

    • action-types.js file ini berisi jenis tindakan (saya menggunakan dacho sebagai cermin kunci)

    • actions.js file ini berisi tindakan negara

    • reducer.js file ini berisi peredam status

    • state2

    • initial.js

    • action-types.js

      ....

Kenapa?
1- Ada 2 jenis tindakan di setiap aplikasi:

  • menyatakan tindakan (redux / state1 / actions.js) tindakan ini bertanggung jawab hanya untuk mengubah keadaan, Anda tidak boleh mengaktifkan tindakan ini secara langsung selalu menggunakan tindakan aplikasi

    • tindakan aplikasi (redux / actions.js) tindakan ini bertanggung jawab untuk melakukan beberapa logika aplikasi dan dapat mengaktifkan tindakan status

2- Selain menggunakan struktur ini, Anda dapat membuat status baru hanya dengan menyalin, menempel folder, dan mengganti namanya;)

Bagaimana jika 2 reduksi perlu mengubah properti yang sama?

Katakanlah saya memiliki peredam yang ingin saya bagi menjadi file berbeda dan ada properti bernama 'error', yang saya ubah saat permintaan http gagal karena satu dan lain alasan.

Sekarang di beberapa titik peredam telah menjadi besar, jadi saya memutuskan untuk membaginya menjadi beberapa pereduksi, tetapi semuanya perlu mengubah properti 'kesalahan' itu.

Lalu apa?

@ Wassap124 Tidak mungkin bagi dua pereduksi untuk mengelola status yang sama. Apakah maksud Anda dua tindakan perlu mengubah properti yang sama?

Tanpa detail lebih lanjut, Anda mungkin lebih suka menggunakan thunk untuk mengirimkan tindakan "set error".

@ rhys-vdw Saya hanya sedikit terjebak saat mencoba memisahkan peredam yang cukup panjang.
Dan mengenai saran Anda, bukankah itu bertentangan dengan aturan tanpa efek samping?

@ Wassap124 , @ rhys-vdw: untuk memperjelas, dua reduksi tidak mungkin mengelola status yang sama _jika Anda hanya menggunakan combineReducers _. Namun, Anda pasti dapat menulis logika peredam lain agar sesuai dengan kasus penggunaan Anda - lihat https://redux.js.org/recipes/structuringreducers/beyondcombinereducers .

Jika Anda melakukan spelunking sumber cepat ...

combineReducers mengembalikan tanda tangan fungsi seperti itu

return function combination(state = {}, action) {

lihat https://github.com/reduxjs/redux/blob/master/src/combineReducers.js#L145

setelah membuat closure untuk mereferensikan objek reduksi yang sebenarnya.

Oleh karena itu, Anda dapat menambahkan sesuatu yang sederhana seperti itu untuk menangani tindakan secara global jika perlu menghidrasi dan memuat ulang status dari penyimpanan.

const globalReducerHandler = () => {
  return (state = {}, action) => {
    if (action.type === 'DO_GLOBAL_THING') {
      return globallyModifyState(state)
    }
    return reducers(state, action)
  }
}

Ini mungkin atau mungkin bukan pola anti tetapi pragmatisme selalu lebih penting.

Alih-alih memanggil peredam dari peredam lain (yang merupakan antipattern, karena globalReducerHandler tidak ada hubungannya dengan peredam yang dipanggilnya), gunakan reduceReducers , yang pada dasarnya compose untuk peredam (yah, itu secara harfiah komposisi dalam (Action ->) monad).

Apakah halaman ini membantu?
0 / 5 - 0 peringkat