Redux: Pendekatan Alternatif Untuk Tindakan Async

Dibuat pada 27 Des 2015  ·  44Komentar  ·  Sumber: reduxjs/redux

Saya telah menjelajahi alternatif cara tindakan async dilakukan di redux, dan saya akan menghargai komentar apa pun yang mungkin dimiliki orang lain tentang apa yang telah saya lakukan.

Untuk mengilustrasikan pendekatan saya, saya telah mengubah contoh async di klon redux saya: https://github.com/winstonewert/redux/tree/master/examples/async

Biasanya, tindakan eksternal dilakukan dengan membuat pembuat tindakan tidak sinkron. Dalam kasus contoh asinkron, pembuat tindakan fetchPosts mengirimkan tindakan REQUEST_POSTS untuk menunjukkan awal dari permintaan, diikuti oleh RECEIVE_POSTS setelah pos kembali dari api.

Dalam contoh saya, semua pembuat tindakan sinkron. Sebagai gantinya, ada fungsi yang mengembalikan daftar tindakan asinkron yang saat ini harus dilakukan berdasarkan status. Lihat contoh saya di sini: https://github.com/rackt/redux/compare/master...winstonewert :master#diff-8a94dc7aa7bdc6e5390c9216a69761f8R12

Fungsi doReactions berlangganan ke store dan memastikan bahwa status aktual dari permintaan yang sedang dibuat cocok dengan status yang dikembalikan oleh status doReactions dengan memulai atau membatalkan permintaan.

Jadi apa bedanya?

1) Fungsi reaksi adalah fungsi murni keadaan. Ini membuatnya mudah untuk diuji.
2) Logika sebenarnya yang membuat permintaan lebih sederhana. Lihat beberapa fungsi baris dalam contoh saya, versus berbagai potongan logika yang disebarkan melalui wadah dan pembuat tindakan sebelumnya.
3) Memudahkan untuk membatalkan permintaan.

Ada pikiran?

discussion feedback wanted

Komentar yang paling membantu

Saya juga telah banyak berpikir tentang cara alternatif untuk menangani efek samping di redux dan saya harap saya tidak membajak utas Anda karena saya membuang beberapa masalah yang saya lihat dengan beberapa pendekatan saat ini dan mengapa dan bagaimana menurut saya ini langkah besar ke arah yang benar meskipun tampak sederhana.

Masalah dengan efek samping dalam pembuat aksi

Dalam bahasa fungsional murni, efek samping selalu diangkat ke tepi aplikasi dan dikembalikan ke runtime untuk dieksekusi. Di Elm, reduksi mengembalikan Tuple yang berisi status baru dan efek apa pun yang harus dieksekusi. Namun, metode dengan tanda tangan ini belum dapat dikomposisi dengan reduksi redux lainnya.

Tempat yang jelas (tetapi mungkin bukan yang terbaik) untuk melakukan efek samping di redux telah menjadi pembuat tindakan dan beberapa opsi middleware yang berbeda telah dikembangkan untuk mendukung pola ini. Namun, saya agak berpikir pendekatan middleware saat ini lebih merupakan solusi karena tidak dapat mengembalikan efek samping sebagai konsep kelas satu dari reduksi.

Sementara orang-orang masih membangun hal-hal luar biasa dengan redux dan ini adalah langkah maju yang besar dan jauh lebih sederhana dan lebih pragmatis daripada kebanyakan alternatif, ada beberapa masalah yang saya lihat dengan memiliki efek samping di dalam pembuat tindakan:

  • Keadaan implisit disembunyikan
  • Duplikasi logika bisnis
  • Asumsi konteks dan/atau dependensi mengurangi penggunaan kembali
  • Pembuat aksi dengan efek samping sulit untuk diuji
  • Tidak dapat dioptimalkan atau dikelompokkan

Keadaan implisit disembunyikan

Dalam aplikasi penghitung, incrementAsync membuat batas waktu dan hanya setelah penyelesaiannya, status aplikasi diperbarui. Jika Anda ingin, misalnya, untuk menampilkan indikator visual bahwa operasi peningkatan sedang berlangsung, tampilan tidak dapat menyimpulkan ini dari status aplikasi. Keadaan ini tersirat dan tersembunyi.

Meskipun terkadang elegan, saya tidak begitu yakin tentang proposal untuk menggunakan generator sebagai orkestra pembuat tindakan karena status implisit disembunyikan dan tidak dapat dengan mudah diserialisasi.

Dengan menggunakan redux-thunk atau yang serupa, Anda dapat mengirimkan beberapa pesan ke peredam yang menginformasikannya saat operasi peningkatan telah dimulai dan ketika telah selesai, tetapi ini menciptakan serangkaian masalah yang berbeda.

Mengembalikan status ke titik di mana operasi kenaikan ditandai sebagai sedang berlangsung setelah efek selesai tidak akan benar-benar membuat ulang efek samping dan dengan demikian akan tetap berlangsung tanpa batas.

Proposal Anda tampaknya menyelesaikan masalah ini. Karena efek samping dibuat dari keadaan, maksud harus diekspresikan dengan keadaan yang dihasilkan dalam beberapa bentuk atau lainnya, jadi jika seseorang mengembalikan penyimpanan ke keadaan sebelumnya di mana tindakan dimulai, maka reaksi akan memulai efek lagi daripada meninggalkan negara dalam limbo.

Duplikasi logika bisnis

Itu wajar untuk tindakan untuk menghasilkan efek samping hanya ketika aplikasi dalam keadaan tertentu. Dalam redux, jika pembuat tindakan memerlukan status, itu harus berupa fungsi yang sederhana dan murni atau secara eksplisit disediakan dengan status.

Sebagai contoh sederhana, katakanlah kita mulai dengan aplikasi penghitung contoh dan kita ingin mengubah penghitung menjadi warna font acak setiap kali penghitung adalah kelipatan 5.

Karena pembuatan angka acak tidak murni, tempat yang disarankan untuk menempatkan perilaku ini adalah di pembuat tindakan. Namun, ada beberapa tindakan berbeda yang dapat mengubah nilai penghitung, kenaikan, penurunan, kenaikanAsync, kenaikanJikaOdd (yang tidak perlu diubah dalam kasus ini).

kenaikan dan penurunan sebelumnya tidak memerlukan status apa pun karena sebelumnya ditangani di peredam dan dengan demikian memiliki akses ke nilai saat ini, tetapi karena peredam tidak dapat memiliki atau mengembalikan efek samping (pembuatan angka acak) fungsi ini sekarang menjadi pembuat tindakan tidak murni yang membutuhkan untuk mengetahui nilai penghitung saat ini untuk menentukan apakah perlu memilih warna font acak baru dan logika ini perlu diduplikasi di semua pembuat tindakan balasan.

Salah satu alternatif yang mungkin untuk secara eksplisit menyediakan status saat ini adalah dengan menggunakan redux-thunk dan mengembalikan panggilan balik untuk mengakses status saat ini. Ini memungkinkan Anda untuk menghindari memodifikasi semua tempat tindakan dibuat untuk memberikan nilai saat ini, tetapi sekarang mengharuskan pembuat tindakan untuk mengetahui di mana dalam keadaan aplikasi global nilai disimpan dan ini membatasi kemampuan untuk menggunakan kembali penghitung yang sama beberapa kali dalam aplikasi yang sama atau dalam aplikasi yang berbeda di mana keadaan mungkin terstruktur secara berbeda.

Asumsi konteks dan/atau dependensi mengurangi penggunaan kembali

Sekali lagi meninjau kembali contoh penghitung Anda akan melihat hanya ada satu contoh penghitung. Meskipun sepele untuk memiliki banyak penghitung pada halaman yang melihat/memperbarui status yang sama, modifikasi tambahan pada penghitung diperlukan jika Anda ingin setiap penghitung menggunakan status yang berbeda.

Ini sudah pernah dibahas sebelumnya Bagaimana cara membuat daftar generik sebagai peredam dan penambah komponen?

Jika penghitung hanya menggunakan jenis tindakan sederhana, akan relatif sepele untuk menerapkan arsitektur elm .

Dalam hal ini orang tua hanya membungkus pembuat tindakan atau operator untuk menambah pesan dengan konteks yang diperlukan, kemudian dapat memanggil peredam secara langsung dengan keadaan lokal.

Sementara Contoh React Elmish tampak mengesankan, yang terutama hilang dari contoh adalah dua pembuat tindakan yang bermasalah, incrementIfOdd dan incrementAsync.

incrementIfOdd bergantung pada middleware untuk menentukan status saat ini dan dengan demikian perlu mengetahui lokasinya dalam status aplikasi.

incrementAsync akhirnya secara langsung mengirimkan tindakan kenaikan yang tidak diekspos ke komponen induk dan dengan demikian tidak dapat dibungkus dengan konteks tambahan.

Meskipun proposal Anda tidak secara langsung mengatasi masalah ini, jika incrementAsync diimplementasikan sebagai tindakan sederhana yang mengubah status menjadi {counter: 0, incrementAfterDelay: 1000} untuk memicu efek samping di pendengar toko, maka incrementAsync menjadi pesan sederhana. incrementIfOdd murni sehingga dapat diimplementasikan dalam peredam atau dapat diberikan status secara eksplisit.... Dengan demikian, dimungkinkan untuk menerapkan arsitektur elm lagi jika diinginkan.

Pembuat aksi dengan efek samping sulit untuk diuji

Saya pikir ini cukup jelas bahwa efek samping akan lebih sulit untuk diuji. Setelah efek samping Anda menjadi tergantung pada keadaan saat ini dan logika bisnis, mereka tidak hanya menjadi lebih sulit tetapi juga lebih penting untuk diuji.

Proposal Anda memungkinkan seseorang untuk dengan mudah membuat pengujian bahwa transisi status akan membuat status yang berisi reaksi yang diinginkan tanpa benar-benar menjalankan salah satu dari mereka. Reaksi juga lebih mudah untuk diuji karena tidak memerlukan kondisi kondisional atau logika bisnis.

Tidak dapat dioptimalkan atau dikelompokkan

Posting blog terbaru dari John A De Goes membahas masalah dengan tipe data buram seperti IO atau Tugas untuk mengekspresikan efek. Dengan menggunakan deskripsi deklaratif efek samping daripada jenis buram, Anda memiliki potensi untuk mengoptimalkan atau menggabungkan efek nanti.

Arsitektur Modern untuk FP

Thunks, promise, dan generator tidak jelas sehingga pengoptimalan seperti mengelompokkan dan/atau menekan panggilan api duplikat harus ditangani secara eksplisit dengan fungsi yang mirip dengan fetchPostsIfNeeded .

Proposal Anda menghilangkan fetchPostsIfNeeded dan tampaknya benar-benar layak untuk menerapkan fungsi reactions yang dapat mengoptimalkan beberapa permintaan dan/atau menggunakan set api yang berbeda sesuai kebutuhan ketika lebih banyak atau lebih sedikit data telah diminta.

Implementasi saya

Saya baru-baru ini membuat garpu redux yang memungkinkan seseorang untuk membuat reduksi yang mengembalikan status baru seperti yang mereka lakukan sekarang atau objek khusus withEffects yang berisi status baru dan deskripsi efek apa pun yang akan dieksekusi setelah peredam.

Saya tidak yakin bagaimana melakukan ini tanpa forking redux karena itu perlu untuk memodifikasi compose dan combineReducers untuk mengangkat efek dari reduksi yang ada untuk menjaga kompatibilitas dengan kode peredam yang ada.

Namun proposal Anda cukup bagus karena tidak memerlukan modifikasi redux. Selain itu, saya pikir solusi Anda melakukan pekerjaan yang lebih baik dalam memecahkan masalah keadaan tersembunyi yang tersirat dan mungkin lebih mudah untuk menggabungkan atau mengoptimalkan reaksi yang dihasilkan.

Ringkasan

Sama seperti React adalah "hanya ui", dan tidak terlalu menentukan bagaimana seseorang benar-benar menyimpan atau memperbarui status aplikasi, Redux sebagian besar "hanya toko" dan tidak terlalu menentukan tentang bagaimana seseorang menangani efek samping.

Saya tidak pernah menyalahkan siapa pun karena bersikap pragmatis dan menyelesaikan pekerjaan dan banyak kontributor untuk redux dan middleware telah memungkinkan orang untuk membangun hal-hal yang sangat keren lebih cepat dan lebih baik daripada yang mungkin dilakukan sebelumnya. Hanya dari kontribusi merekalah kami sampai sejauh ini. Jadi terima kasih khusus untuk semua orang yang telah berkontribusi.

Redux luar biasa. Ini bukan masalah yang perlu dengan Redux itu sendiri, tetapi semoga kritik konstruktif dari pola arsitektur saat ini dan motivasi serta keuntungan potensial untuk menjalankan efek setelah daripada sebelum modifikasi keadaan.

Semua 44 komentar

Pendekatan yang menarik! Sepertinya itu idealnya harus dipisahkan dari sifat asinkron, seperti metode yang digunakan untuk menjalankan XHR, atau bahkan permintaan web adalah sumber asinkron.

Saya juga telah banyak berpikir tentang cara alternatif untuk menangani efek samping di redux dan saya harap saya tidak membajak utas Anda karena saya membuang beberapa masalah yang saya lihat dengan beberapa pendekatan saat ini dan mengapa dan bagaimana menurut saya ini langkah besar ke arah yang benar meskipun tampak sederhana.

Masalah dengan efek samping dalam pembuat aksi

Dalam bahasa fungsional murni, efek samping selalu diangkat ke tepi aplikasi dan dikembalikan ke runtime untuk dieksekusi. Di Elm, reduksi mengembalikan Tuple yang berisi status baru dan efek apa pun yang harus dieksekusi. Namun, metode dengan tanda tangan ini belum dapat dikomposisi dengan reduksi redux lainnya.

Tempat yang jelas (tetapi mungkin bukan yang terbaik) untuk melakukan efek samping di redux telah menjadi pembuat tindakan dan beberapa opsi middleware yang berbeda telah dikembangkan untuk mendukung pola ini. Namun, saya agak berpikir pendekatan middleware saat ini lebih merupakan solusi karena tidak dapat mengembalikan efek samping sebagai konsep kelas satu dari reduksi.

Sementara orang-orang masih membangun hal-hal luar biasa dengan redux dan ini adalah langkah maju yang besar dan jauh lebih sederhana dan lebih pragmatis daripada kebanyakan alternatif, ada beberapa masalah yang saya lihat dengan memiliki efek samping di dalam pembuat tindakan:

  • Keadaan implisit disembunyikan
  • Duplikasi logika bisnis
  • Asumsi konteks dan/atau dependensi mengurangi penggunaan kembali
  • Pembuat aksi dengan efek samping sulit untuk diuji
  • Tidak dapat dioptimalkan atau dikelompokkan

Keadaan implisit disembunyikan

Dalam aplikasi penghitung, incrementAsync membuat batas waktu dan hanya setelah penyelesaiannya, status aplikasi diperbarui. Jika Anda ingin, misalnya, untuk menampilkan indikator visual bahwa operasi peningkatan sedang berlangsung, tampilan tidak dapat menyimpulkan ini dari status aplikasi. Keadaan ini tersirat dan tersembunyi.

Meskipun terkadang elegan, saya tidak begitu yakin tentang proposal untuk menggunakan generator sebagai orkestra pembuat tindakan karena status implisit disembunyikan dan tidak dapat dengan mudah diserialisasi.

Dengan menggunakan redux-thunk atau yang serupa, Anda dapat mengirimkan beberapa pesan ke peredam yang menginformasikannya saat operasi peningkatan telah dimulai dan ketika telah selesai, tetapi ini menciptakan serangkaian masalah yang berbeda.

Mengembalikan status ke titik di mana operasi kenaikan ditandai sebagai sedang berlangsung setelah efek selesai tidak akan benar-benar membuat ulang efek samping dan dengan demikian akan tetap berlangsung tanpa batas.

Proposal Anda tampaknya menyelesaikan masalah ini. Karena efek samping dibuat dari keadaan, maksud harus diekspresikan dengan keadaan yang dihasilkan dalam beberapa bentuk atau lainnya, jadi jika seseorang mengembalikan penyimpanan ke keadaan sebelumnya di mana tindakan dimulai, maka reaksi akan memulai efek lagi daripada meninggalkan negara dalam limbo.

Duplikasi logika bisnis

Itu wajar untuk tindakan untuk menghasilkan efek samping hanya ketika aplikasi dalam keadaan tertentu. Dalam redux, jika pembuat tindakan memerlukan status, itu harus berupa fungsi yang sederhana dan murni atau secara eksplisit disediakan dengan status.

Sebagai contoh sederhana, katakanlah kita mulai dengan aplikasi penghitung contoh dan kita ingin mengubah penghitung menjadi warna font acak setiap kali penghitung adalah kelipatan 5.

Karena pembuatan angka acak tidak murni, tempat yang disarankan untuk menempatkan perilaku ini adalah di pembuat tindakan. Namun, ada beberapa tindakan berbeda yang dapat mengubah nilai penghitung, kenaikan, penurunan, kenaikanAsync, kenaikanJikaOdd (yang tidak perlu diubah dalam kasus ini).

kenaikan dan penurunan sebelumnya tidak memerlukan status apa pun karena sebelumnya ditangani di peredam dan dengan demikian memiliki akses ke nilai saat ini, tetapi karena peredam tidak dapat memiliki atau mengembalikan efek samping (pembuatan angka acak) fungsi ini sekarang menjadi pembuat tindakan tidak murni yang membutuhkan untuk mengetahui nilai penghitung saat ini untuk menentukan apakah perlu memilih warna font acak baru dan logika ini perlu diduplikasi di semua pembuat tindakan balasan.

Salah satu alternatif yang mungkin untuk secara eksplisit menyediakan status saat ini adalah dengan menggunakan redux-thunk dan mengembalikan panggilan balik untuk mengakses status saat ini. Ini memungkinkan Anda untuk menghindari memodifikasi semua tempat tindakan dibuat untuk memberikan nilai saat ini, tetapi sekarang mengharuskan pembuat tindakan untuk mengetahui di mana dalam keadaan aplikasi global nilai disimpan dan ini membatasi kemampuan untuk menggunakan kembali penghitung yang sama beberapa kali dalam aplikasi yang sama atau dalam aplikasi yang berbeda di mana keadaan mungkin terstruktur secara berbeda.

Asumsi konteks dan/atau dependensi mengurangi penggunaan kembali

Sekali lagi meninjau kembali contoh penghitung Anda akan melihat hanya ada satu contoh penghitung. Meskipun sepele untuk memiliki banyak penghitung pada halaman yang melihat/memperbarui status yang sama, modifikasi tambahan pada penghitung diperlukan jika Anda ingin setiap penghitung menggunakan status yang berbeda.

Ini sudah pernah dibahas sebelumnya Bagaimana cara membuat daftar generik sebagai peredam dan penambah komponen?

Jika penghitung hanya menggunakan jenis tindakan sederhana, akan relatif sepele untuk menerapkan arsitektur elm .

Dalam hal ini orang tua hanya membungkus pembuat tindakan atau operator untuk menambah pesan dengan konteks yang diperlukan, kemudian dapat memanggil peredam secara langsung dengan keadaan lokal.

Sementara Contoh React Elmish tampak mengesankan, yang terutama hilang dari contoh adalah dua pembuat tindakan yang bermasalah, incrementIfOdd dan incrementAsync.

incrementIfOdd bergantung pada middleware untuk menentukan status saat ini dan dengan demikian perlu mengetahui lokasinya dalam status aplikasi.

incrementAsync akhirnya secara langsung mengirimkan tindakan kenaikan yang tidak diekspos ke komponen induk dan dengan demikian tidak dapat dibungkus dengan konteks tambahan.

Meskipun proposal Anda tidak secara langsung mengatasi masalah ini, jika incrementAsync diimplementasikan sebagai tindakan sederhana yang mengubah status menjadi {counter: 0, incrementAfterDelay: 1000} untuk memicu efek samping di pendengar toko, maka incrementAsync menjadi pesan sederhana. incrementIfOdd murni sehingga dapat diimplementasikan dalam peredam atau dapat diberikan status secara eksplisit.... Dengan demikian, dimungkinkan untuk menerapkan arsitektur elm lagi jika diinginkan.

Pembuat aksi dengan efek samping sulit untuk diuji

Saya pikir ini cukup jelas bahwa efek samping akan lebih sulit untuk diuji. Setelah efek samping Anda menjadi tergantung pada keadaan saat ini dan logika bisnis, mereka tidak hanya menjadi lebih sulit tetapi juga lebih penting untuk diuji.

Proposal Anda memungkinkan seseorang untuk dengan mudah membuat pengujian bahwa transisi status akan membuat status yang berisi reaksi yang diinginkan tanpa benar-benar menjalankan salah satu dari mereka. Reaksi juga lebih mudah untuk diuji karena tidak memerlukan kondisi kondisional atau logika bisnis.

Tidak dapat dioptimalkan atau dikelompokkan

Posting blog terbaru dari John A De Goes membahas masalah dengan tipe data buram seperti IO atau Tugas untuk mengekspresikan efek. Dengan menggunakan deskripsi deklaratif efek samping daripada jenis buram, Anda memiliki potensi untuk mengoptimalkan atau menggabungkan efek nanti.

Arsitektur Modern untuk FP

Thunks, promise, dan generator tidak jelas sehingga pengoptimalan seperti mengelompokkan dan/atau menekan panggilan api duplikat harus ditangani secara eksplisit dengan fungsi yang mirip dengan fetchPostsIfNeeded .

Proposal Anda menghilangkan fetchPostsIfNeeded dan tampaknya benar-benar layak untuk menerapkan fungsi reactions yang dapat mengoptimalkan beberapa permintaan dan/atau menggunakan set api yang berbeda sesuai kebutuhan ketika lebih banyak atau lebih sedikit data telah diminta.

Implementasi saya

Saya baru-baru ini membuat garpu redux yang memungkinkan seseorang untuk membuat reduksi yang mengembalikan status baru seperti yang mereka lakukan sekarang atau objek khusus withEffects yang berisi status baru dan deskripsi efek apa pun yang akan dieksekusi setelah peredam.

Saya tidak yakin bagaimana melakukan ini tanpa forking redux karena itu perlu untuk memodifikasi compose dan combineReducers untuk mengangkat efek dari reduksi yang ada untuk menjaga kompatibilitas dengan kode peredam yang ada.

Namun proposal Anda cukup bagus karena tidak memerlukan modifikasi redux. Selain itu, saya pikir solusi Anda melakukan pekerjaan yang lebih baik dalam memecahkan masalah keadaan tersembunyi yang tersirat dan mungkin lebih mudah untuk menggabungkan atau mengoptimalkan reaksi yang dihasilkan.

Ringkasan

Sama seperti React adalah "hanya ui", dan tidak terlalu menentukan bagaimana seseorang benar-benar menyimpan atau memperbarui status aplikasi, Redux sebagian besar "hanya toko" dan tidak terlalu menentukan tentang bagaimana seseorang menangani efek samping.

Saya tidak pernah menyalahkan siapa pun karena bersikap pragmatis dan menyelesaikan pekerjaan dan banyak kontributor untuk redux dan middleware telah memungkinkan orang untuk membangun hal-hal yang sangat keren lebih cepat dan lebih baik daripada yang mungkin dilakukan sebelumnya. Hanya dari kontribusi merekalah kami sampai sejauh ini. Jadi terima kasih khusus untuk semua orang yang telah berkontribusi.

Redux luar biasa. Ini bukan masalah yang perlu dengan Redux itu sendiri, tetapi semoga kritik konstruktif dari pola arsitektur saat ini dan motivasi serta keuntungan potensial untuk menjalankan efek setelah daripada sebelum modifikasi keadaan.

Saya mencoba memahami perbedaan antara pendekatan ini dan redux-saga. Saya tertarik dengan klaim bahwa ia menyembunyikan status di generator secara implisit, karena pada awalnya, sepertinya ia melakukan hal yang sama. Tapi saya kira itu mungkin tergantung pada bagaimana io.take diimplementasikan. Jika saga hanya akan memproses suatu tindakan jika kebetulan saat ini diblokir pada yield , maka saya pasti mengerti maksud Anda. Tetapi jika tindakan antrian redux-saga sedemikian rupa sehingga io.take akan mengembalikan tindakan sebelumnya, sepertinya ia melakukan hal yang sama. Either way, Anda punya beberapa logika yang dapat dispatch tindakan asinkron, dipicu dengan mendengarkan aliran tindakan.

Ini adalah konsep yang menarik sekalipun. Mengkonseptualisasikan Redux sebagai aliran tindakan, dari mana transisi dan efek status dipicu. Bagi saya itu adalah pandangan alternatif daripada hanya menganggapnya sebagai prosesor negara.

Dalam model sumber acara, saya pikir itu bermuara pada apakah tindakan Redux adalah "perintah" (permintaan kontingen untuk mengambil tindakan) atau "peristiwa" (transisi atom keadaan, tercermin dalam tampilan datar). Saya kira kami memiliki alat yang cukup fleksibel untuk dipikirkan.

Saya juga sedikit tidak puas dengan status quo "pembuat tindakan cerdas", tetapi saya telah mendekatinya dengan cara yang berbeda, di mana Redux lebih merupakan toko acara -- di mana tindakan adalah salah satu dari banyak kemungkinan efek yang mungkin dipicu oleh beberapa lapisan "pengontrol" eksternal. Saya memfaktorkan kode yang mengikuti pendekatan ini ke react-redux-controller , meskipun saya punya ide setengah matang tentang cara yang berpotensi lebih ringan untuk mencapai ini. Namun, itu akan membutuhkan react-redux untuk memiliki pengait yang saat ini tidak dimilikinya, dan beberapa pesta pora pembungkus toko yang belum saya kerjakan.

Toko pesta pora dijelaskan https://github.com/rackt/redux/issues/1200

Saya mencoba memahami perbedaan antara pendekatan ini dan redux-saga

Saya tidak melihat redux-saga sampai saya menemukan pendekatan saya, tetapi pasti ada beberapa kesamaan. Tapi saya masih memiliki beberapa perbedaan:

  1. Pendekatan saya tidak memiliki akses ke aliran tindakan, hanya ke negara bagian. redux-saga dapat memulai proses hanya karena ada tindakan. Pendekatan saya mengharuskan peredam membuat perubahan ke keadaan yang memicu fungsi reaksi untuk meminta tindakan.
  2. Pendekatan saya mengharuskan semua status ada di status redux. Redux-saga memiliki status tambahan yang hidup di generator saga (di baris mana ia berada, nilai-nilai variabel lokal).
  3. Pendekatan saya mengisolasi bagian asinkron. Logika reaksi yang sebenarnya dapat diuji tanpa berurusan dengan fungsi asinkron. Saga menyatukan ini.
  4. Kisah ini menyatukan potongan-potongan berbeda dari logika yang sama. Pendekatan saya memaksa Anda untuk membagi saga menjadi beberapa bagian yang termasuk dalam implementasi tipe peredam, reaksi, dan reaksi.

Pada dasarnya, pendekatan saya menekankan fungsi murni dan menjaga semuanya dalam keadaan redux. Pendekatan redux-saga menekankan menjadi lebih ekspresif. Saya pikir ada pro dan kontra, tapi saya lebih suka milik saya. Tapi aku bias.

Kedengarannya sangat menjanjikan. Saya pikir akan lebih menarik untuk melihat contoh yang memisahkan mesin reaksi dari logika domain.

Proposal Anda menghilangkan fetchPostsIfNeeded dan tampaknya benar-benar layak untuk menerapkan fungsi reaksi yang dapat mengoptimalkan beberapa permintaan dan/atau menggunakan kumpulan api yang berbeda sesuai kebutuhan ketika lebih banyak atau lebih sedikit data telah diminta.

Seperti berdiri, Anda tidak bisa benar-benar melakukan itu dalam fungsi reaksi. Logika di sana perlu mengetahui tindakan mana yang sudah dimulai (kita tidak dapat mengelompokkan apa pun lagi ke dalamnya), tetapi fungsi reaksi tidak memiliki informasinya. Mesin reaksi yang menggunakan fungsi reaksi() pasti bisa melakukan hal itu.

Saya pikir akan lebih menarik untuk melihat contoh yang memisahkan mesin reaksi dari logika domain.

Saya berasumsi maksud Anda cara fungsi doReactions() menangani mulai/berhenti XMLHttpRequest? Saya telah menjelajahi berbagai cara untuk melakukan itu. Masalahnya adalah sulit untuk menemukan cara umum untuk mendeteksi apakah dua reaksi sebenarnya adalah reaksi yang sama. isEqual Lodash hampir berfungsi, tetapi gagal untuk penutupan.

Saya berasumsi maksud Anda cara fungsi doReactions() menangani mulai/berhenti XMLHttpRequest?

Tidak, maksud saya hanya dalam contoh Anda, semua konfigurasi untuk menyiapkan konsep reaksi dicampur dengan logika domain dari data apa yang diambil, serta detail tentang bagaimana data itu diambil. Tampaknya bagi saya bahwa aspek generik harus diperhitungkan menjadi sesuatu yang kurang digabungkan dengan detail khusus untuk contoh.

Tidak, maksud saya hanya dalam contoh Anda, semua konfigurasi untuk menyiapkan konsep reaksi dicampur dengan logika domain dari data apa yang diambil, serta detail tentang bagaimana data itu diambil. Tampaknya bagi saya bahwa aspek generik harus diperhitungkan menjadi sesuatu yang kurang digabungkan dengan detail khusus untuk contoh.

Hmm... Saya pikir kita mungkin tidak mengartikan hal yang sama dengan logika domain.

Cara saya melihatnya, fungsi reaction() merangkum logika domain, dan terpisah dari fungsi doReactions() yang menangani logika bagaimana reaksi diterapkan. Tapi sepertinya maksudmu berbeda...

Seperti berdiri, Anda tidak bisa benar-benar melakukan itu dalam fungsi reaksi. Logika di sana perlu mengetahui tindakan mana yang sudah dimulai (kita tidak dapat mengelompokkan apa pun lagi ke dalamnya), tetapi fungsi reaksi tidak memiliki informasinya. Mesin reaksi yang menggunakan fungsi reaksi() pasti bisa melakukan hal itu.

Maksud saya kebanyakan bahwa jika satu peristiwa memicu perubahan keadaan di mana beberapa komponen meminta informasi yang sama maka mungkin dapat mengoptimalkannya. Namun Anda benar bahwa itu sendiri tidak cukup untuk menentukan apakah efek samping dari perubahan status sebelumnya masih tertunda dan dengan demikian permintaan tambahan tidak diperlukan.

Saya awalnya berpikir mungkin orang bisa menyimpan semua negara dalam negara aplikasi, tetapi ketika saya mulai berpikir tentang baru-baru ini masalah stopwatch saya menyadari bahwa sementara fakta bahwa stopwatch isOn harus disimpan dalam keadaan aplikasi yang sebenarnya interval objek yang terkait dengan stopwatch ini perlu disimpan di tempat lain. isOn harus dalam status aplikasi tetapi tidak sendirian tidak cukup dalam kasus ini.

Maksud saya kebanyakan bahwa jika satu peristiwa memicu perubahan keadaan di mana beberapa komponen meminta informasi yang sama maka mungkin dapat mengoptimalkannya. Namun Anda benar bahwa itu sendiri tidak cukup untuk menentukan apakah efek samping dari perubahan status sebelumnya masih tertunda dan dengan demikian permintaan tambahan tidak diperlukan.

Saya sedang berpikir untuk menggabungkan atau mengelompokkan permintaan. Menghilangkan duplikat seharusnya berfungsi dengan baik. Sebenarnya, itu harus menangani kasus perubahan status yang tertunda dengan baik juga, karena mereka masih akan dikembalikan dari fungsi reaksi (dan dengan demikian tidak diduplikasi) hingga respons server kembali.

Saya awalnya berpikir mungkin seseorang dapat menyimpan semua status dalam status aplikasi, tetapi ketika saya mulai memikirkan masalah stopwatch baru-baru ini, saya menyadari bahwa sementara fakta bahwa stopwatch isOn harus disimpan dalam status aplikasi, objek interval aktual yang terkait dengan stopwatch ini perlu disimpan di tempat lain. isOn harus dalam status aplikasi tetapi tidak sendirian tidak cukup dalam kasus ini.

Cara saya memikirkannya, reaksi yang tertunda saat ini seperti komponen reaksi Anda. Secara teknis mereka memiliki beberapa keadaan internal, tetapi kami memodelkannya sebagai fungsi dari keadaan saat ini.

Hmm... Saya pikir kita mungkin tidak mengartikan hal yang sama dengan logika domain.

Cara saya melihatnya, fungsi reaction() merangkum logika domain, dan terpisah dari fungsi doReactions() yang menangani logika bagaimana reaksi diterapkan. Tapi sepertinya maksudmu berbeda...

Saya agak mengambil seluruh modul /reactions/index secara keseluruhan, tapi ya, saya setuju bahwa fungsi reactions adalah murni logika domain. Namun alih-alih berada dalam modul khusus domain, modul ini digabungkan dengan boilerplate doReactions . Itu bukan untuk menjatuhkan metodologi Anda, itu hanya membuat lebih sulit untuk memahami sekilas pemisahan antara kode perpustakaan dan kode aplikasi.

Kemudian doReactions itu sendiri menurut saya agak terkait erat dengan metode tertentu dari tindakan tertentu yang mengambil data dari API. Saya akan mengira bahwa perpustakaan reaksi yang disempurnakan mungkin merupakan cara mendaftarkan penangan untuk berbagai jenis efek.

Itu bukan untuk menjatuhkan metode Anda; Saya menemukan pendekatan ini sangat menarik.

Saya tidak yakin keadaan komponen reaksi adalah analogi yang baik karena sebagian besar keadaan reaksi
harus dalam status aplikasi, tetapi tampaknya perlu ada beberapa cara
untuk mempertahankan status antara peristiwa pengiriman yang tidak dapat ditempatkan di
toko.

Saya pikir keadaan seperti ini adalah apa yang @yelouafi sebut sebagai keadaan kontrol dan
mungkin saga adalah cara yang oke untuk memodelkan keadaan yang tidak dapat serial
sistem sebagai pengamat/pelaku independen.

Saya pikir saya akan kurang peduli tentang keadaan saga tersembunyi jika saga
hanya menanggapi peristiwa yang dihasilkan aplikasi (reaksi) alih-alih pengguna
peristiwa yang dimulai (tindakan) karena ini akan memungkinkan peredam aplikasi untuk menggunakan
keadaan saat ini dan logika bisnis bersyarat apa pun untuk menentukan apakah
aplikasi harus memungkinkan efek samping yang diinginkan tanpa menduplikasi
logika bisnis.
Pada Senin, 4 Januari 2016 pukul 17:56 Winston Ewert [email protected]
menulis:

Maksud saya kebanyakan bahwa jika satu peristiwa memicu perubahan keadaan di mana
beberapa komponen meminta informasi yang sama maka mungkin bisa
mengoptimalkan mereka. Namun Anda benar bahwa itu sendiri tidak cukup untuk
menentukan apakah efek samping dari perubahan status sebelumnya masih tertunda
dan dengan demikian permintaan tambahan tidak diperlukan.

Saya sedang berpikir untuk menggabungkan atau mengelompokkan permintaan. Menghilangkan duplikat
harus bekerja dengan baik. Sebenarnya, itu harus menangani kasus status tertunda
berubah dengan baik juga, karena mereka masih akan dikembalikan dari
reaksi berfungsi (dan dengan demikian tidak diduplikasi) hingga respons server
datang kembali.

Saya awalnya berpikir mungkin seseorang dapat menyimpan semua status di dalam aplikasi
negara, tetapi ketika saya mulai berpikir tentang masalah stopwatch baru-baru ini, saya
menyadari bahwa sementara fakta bahwa stopwatch isOn harus disimpan di
aplikasi menyatakan objek interval aktual yang terkait dengan ini
stopwatch perlu disimpan di tempat lain. isOn harus ada di aplikasi
negara tetapi tidak sendirian tidak cukup negara dalam kasus ini.

Cara saya memikirkannya, reaksi yang tertunda saat ini seperti Anda
komponen reaksi. Secara teknis mereka memiliki beberapa keadaan internal, tetapi kami memodelkan
mereka sebagai fungsi dari keadaan saat ini.


Balas email ini secara langsung atau lihat di GitHub
https://github.com/rackt/redux/issues/1182#issuecomment -168858051.

Itu bukan untuk menjatuhkan metodologi Anda, itu hanya membuat lebih sulit untuk memahami sekilas pemisahan antara kode perpustakaan dan kode aplikasi.

Itu benar-benar adil.

Kemudian doReactions sendiri menurut saya agak terkait erat dengan metode tertentu dari tindakan tertentu yang mengambil data dari API. Saya akan mengira bahwa perpustakaan reaksi yang disempurnakan mungkin merupakan cara mendaftarkan penangan untuk berbagai jenis efek.

Ya. Saya masih mencoba mencari cara terbaik untuk membaginya. Ini diperumit oleh masalah pemeriksaan kesetaraan.

Saya tidak yakin keadaan komponen reaksi adalah analogi yang baik karena sebagian besar keadaan reaksi
harus dalam status aplikasi, tetapi tampaknya perlu ada beberapa cara
untuk mempertahankan status antara peristiwa pengiriman yang tidak dapat ditempatkan di
toko.

Maaf, saya pikir saya mengacaukan analogi. Maksud saya bukanlah untuk membandingkan keadaan tindakan eksternal untuk bereaksi terhadap keadaan komponen seperti halnya keadaan DOM. Interval atau XMLHttpRequest agak seperti elemen DOM yang bereaksi membuat dan menghancurkan. Anda cukup memberi tahu reaksi seperti apa DOM saat ini dan mewujudkannya. Demikian juga, Anda cukup mengembalikan rangkaian reaksi eksternal saat ini, dan kerangka kerja membatalkan atau memulai tindakan untuk membuatnya benar.

Saya menemukan pendekatan itu sangat menarik juga. Sudahkah Anda mempertimbangkan untuk menggunakan beberapa doReactions , yang mengambil pemetaan status berbeda? Saya pikir ini akan mirip dengan cyclejs, di mana Anda dapat membuat driver yang dapat digunakan kembali:

function main(action$) {
  const state$ = action$.startWith(INITIAL_STATE).scan(reducer);

  return { 
    DOM: state$.map(describeDOM),
    HTTP: state$.map(describeRequests),
    ...
  };
}

Satu perbedaannya adalah Anda tidak meminta driver untuk acara untuk mendapatkan aliran tindakan ( const someEvent$ = sources.DOM.select('.class').events('click') ), tetapi tentukan tindakan di wastafel secara langsung ( <button onClick={() => dispatch(action())} /> ) seperti yang telah Anda lakukan untuk permintaan HTTP demikian juga.

Saya pikir analogi React bekerja dengan cukup baik. Saya tidak akan menganggap DOM sebagai status internal, melainkan API yang digunakannya, sedangkan status internal terdiri dari instance komponen dan dom virtual.

Inilah ide untuk API (menggunakan React; HTTP juga dapat dibuat seperti ini):

// usage
const describe = (state, dispatch) => <MyComponent state={state} dispatch={dispatch} />;
const driver = createReactDOMDriver({ container } /* opts */);
store.subscribe(() => driver.update(describe(store.getState(), store.dispatch)); 
// (could be simplified further to eg. `store.use(driver, describe)` )

// implementation
const createReactDOMDriver = ({ container }) => {
  return {
    update: (element) => ReactDOM.render(element, container),
    destroy: () => ReactDOM.unmountComponentAtNode(container),
  };
};

Saya akan meminta describe mengambil getState (daripada snapshot negara bagian) dan dispatch . Dengan begitu, itu bisa asinkron seperti yang diinginkan.

Sudahkah Anda mempertimbangkan untuk menggunakan beberapa doReactions, yang mengambil pemetaan status berbeda?

Saya telah memikirkannya secara singkat, dan saya akan bolak-balik sedikit sekarang. Wajar jika memiliki perpustakaan reaksi berbeda yang melakukan hal berbeda, satu untuk DOM, satu untuk http, satu untuk timer, satu untuk audio web, dll. Masing-masing dapat melakukan optimasi/perilaku yang sesuai dengan kasusnya sendiri. Tetapi tampaknya kurang membantu jika Anda memiliki aplikasi yang melakukan banyak tindakan eksternal satu kali.

Saya akan meminta deskripsi take getState (daripada snapshot status) dan pengiriman. Dengan begitu, itu bisa asinkron seperti yang diinginkan.

saya tidak akan. Dalam pandangan saya, kami ingin membatasi async jika memungkinkan, tidak memberikan cara tambahan untuk menggunakannya. Apa pun yang Anda ingin panggil getState() harus dilakukan di fungsi peredam atau reaksi. (Tapi itu pola pikir murni saya, dan mungkin ada alasan pragmatis untuk tidak mengikutinya.)

Poin yang adil. Saya belum cukup memikirkan pemetaan antara ide Anda dan contoh @taurose . Saya buru-buru berasumsi describe adalah fungsi reactions , tetapi itu mungkin tidak benar.

Tapi ya, saya setuju bahwa membatasi async itu ideal, karena jika saya memahami dorongan ide Anda, kami ingin kelanjutannya murni dan memetakan 1:1 dengan aspek-aspek tertentu di negara bagian, seperti kehadiran anggota array yang menjelaskan maksud itu efek tertentu sedang berlangsung. Dengan begitu, tidak masalah jika dijalankan beberapa kali, dan tidak ada aspek tersembunyi dari proses yang terhenti di suatu tempat di tengah aliran yang mungkin bergantung pada proses lain secara implisit.

Saya akan meminta deskripsi take getState (daripada snapshot status) dan pengiriman. Dengan begitu, itu bisa asinkron seperti yang diinginkan.

describe dipanggil pada setiap perubahan status, jadi saya tidak melihat perlunya itu. Bukan berarti tidak bisa melakukan async. Pertimbangkan komponen reaksi: Anda tidak akan memanggil getState di dalam metode render atau event handler Anda untuk mendapatkan status saat ini, melainkan membacanya dari props.

Tetapi Anda benar bahwa itu tidak dapat (tidak boleh) melakukan apa pun secara asinkron dengan sendirinya; itu harus menyerahkannya kepada pengemudi dan hanya memberikannya beberapa status yang dipetakan dan/atau panggilan balik.

diasumsikan menggambarkan adalah fungsi reaksi, tapi itu mungkin tidak benar.

Sejauh yang saya tahu, itu hampir sama. Satu perbedaannya adalah reactions tidak mendapatkan dispatch . Jadi sementara describe mengembalikan callback yang membuat dan mengirimkan tindakan, reactions mengembalikan pembuat tindakan.

@winstonewert ini adalah utas yang panjang dan saya tidak punya waktu untuk membaca sekarang atau memeriksa kode Anda, tetapi mungkin @yelouafi dapat menjawab Anda.

Proyek redux-saga berasal dari diskusi panjang di sini

Saya juga menggunakan konsep saga selama lebih dari setahun pada aplikasi produksi, dan implementasinya kurang ekspresif tetapi tidak berdasarkan generator. Berikut adalah beberapa contoh semu yang saya berikan tentang konsep redux:

Implementasi di sini jauh dari sempurna tetapi hanya memberikan gambaran.

@yelouafi menyadari masalah yang melekat pada penggunaan generator yang menyembunyikan status di luar redux, dan rumitnya memulai kisah di backend, dan mengirimkan status tersembunyi itu ke frontend untuk aplikasi universal (jika benar-benar diperlukan?)

Redux-saga adalah untuk redux-thunk seperti Free adalah untuk IO monad. Efeknya bersifat deklaratif dan tidak dieksekusi saat ini, dapat diintrospeksi dan dijalankan dalam juru bahasa (yang dapat Anda sesuaikan di masa mendatang)

Saya mengerti maksud Anda tentang keadaan tersembunyi di dalam generator. Tetapi sebenarnya apakah Redux menyimpan sumber kebenaran sebenarnya dari aplikasi Redux? Saya tidak berpikir begitu. Redux merekam tindakan, dan memutar ulang. Anda selalu dapat memutar ulang tindakan ini untuk membuat ulang toko. Penyimpanan redux seperti tampilan kueri CQRS dari log peristiwa. Itu tidak berarti itu harus menjadi satu-satunya proyeksi log peristiwa itu. Anda dapat memproyeksikan log peristiwa yang sama dalam tampilan kueri yang berbeda, dan mendengarkannya dalam saga yang dapat mengelola statusnya dengan generator, objek atau reduksi global yang dapat diubah, apa pun teknologinya.

Imho menciptakan konsep saga dengan peredam bukanlah ide yang buruk secara konseptual, dan saya setuju dengan Anda itu adalah keputusan tradeof.
Secara pribadi setelah lebih dari 1 tahun menggunakan saga dalam produksi, saya tidak ingat usecase apa pun yang akan berguna untuk dapat memotret keadaan saga dan mengembalikannya nanti, jadi saya lebih suka ekspresif generator bahkan jika saya kehilangan ini fitur.

Saya harap tidak ada yang saya katakan sebagai serangan terhadap redux-saga. Saya hanya berbicara tentang perbedaannya dari pendekatan yang saya buat.

Saya mengerti maksud Anda tentang keadaan tersembunyi di dalam generator. Tetapi sebenarnya apakah Redux menyimpan sumber kebenaran sebenarnya dari aplikasi Redux? Saya tidak berpikir begitu. Redux merekam tindakan, dan memutar ulang. Anda selalu dapat memutar ulang tindakan ini untuk membuat ulang toko. Penyimpanan redux seperti tampilan kueri CQRS dari log peristiwa. Itu tidak berarti itu harus menjadi satu-satunya proyeksi log peristiwa itu. Anda dapat memproyeksikan log peristiwa yang sama dalam tampilan kueri yang berbeda, dan mendengarkannya dalam saga yang dapat mengelola statusnya dengan generator, objek atau reduksi global yang dapat diubah, apa pun teknologinya.

Saya tidak begitu mengerti maksud Anda di sini. Anda sepertinya berargumen bahwa saga adalah proyeksi dari log peristiwa? Tapi tidak. Jika saya memutar ulang tindakan, saya tidak akan sampai ke tempat yang sama di saga jika saga bergantung pada peristiwa asinkron. Bagi saya tampaknya tak terhindarkan bahwa saga menghasilkan status yang tidak ada di penyimpanan status redux atau proyeksi log peristiwa.

Sejauh yang saya tahu, itu hampir sama. Satu perbedaannya adalah bahwa reaksi tidak terkirim. Jadi, sementara deskripsikan mengembalikan panggilan balik yang membuat dan mengirim tindakan, reaksi mengembalikan pembuat tindakan.

Sepakat. Pada prinsipnya, reaksi dapat menggunakan antarmuka yang sama, semua pengendali acara akan mengambil pembuat tindakan yang akan dikirim ketika acara dipecat.

Semakin saya memikirkan hal ini, saya pikir akan ada banyak sinergi antara pendekatan ini dan kisah-kisah. Saya sepenuhnya setuju dengan empat poin yang digariskan oleh @winstonewert. Saya pikir itu adalah hal yang baik bahwa reaksi tidak dapat melihat tindakan yang dimulai pengguna karena ini mencegah keadaan tersembunyi dan memastikan bahwa logika bisnis di reduksi tidak perlu diduplikasi dalam pembuat tindakan atau saga. Namun, saya menyadari bahwa efek samping sering membuat status tidak dapat diserialkan yang tidak dapat disimpan di penyimpanan reaksi, interval, objek dom, permintaan http, dll. saga, rxjs, baconjs, dll. sempurna untuk status kontrol eksternal yang tidak dapat diserialisasi ini.

doReactions dapat diganti dengan saga dan sumber peristiwa untuk saga harus berupa reaksi, bukan aksi.

Saya harap tidak ada yang saya katakan yang dianggap sebagai serangan terhadap redux-saga

Tidak semuanya. Saya telah mengikuti diskusi tetapi tidak ingin berkomentar tanpa melihat lebih dekat ke kode Anda.

Sekilas. Tampaknya Anda hanya bereaksi terhadap perubahan status. Seperti yang saya katakan itu adalah tampilan cepat. Tetapi tampaknya itu akan membuat penerapan aliran kompleks menjadi lebih sulit daripada pendekatan elm (di mana Anda mengambil status dan tindakan). ini berarti Anda harus menyimpan lebih banyak status kontrol ke dalam penyimpanan (di mana perubahan status aplikasi saja tidak cukup untuk menyimpulkan reaksi yang relevan)

Tentu, tidak ada yang bisa mengalahkan fungsi murni. Saya pikir reduksi sangat bagus untuk mengekspresikan transisi status tetapi menjadi sangat aneh ketika Anda mengubahnya menjadi mesin status.

ini berarti Anda harus menyimpan lebih banyak status kontrol ke dalam penyimpanan (di mana perubahan status aplikasi saja tidak cukup untuk menyimpulkan reaksi yang relevan)

Ya. Bagi saya ini adalah aspek pembeda utama dari pendekatan ini. Tetapi saya bertanya-tanya apakah masalah ini dapat dibuat transparan, dalam praktiknya, jika jenis efek yang berbeda dapat dibungkus dalam "driver" yang berbeda? Saya membayangkan cukup mudah bagi orang untuk memilih driver yang mereka inginkan atau menulis sendiri untuk efek baru.

Namun, saya menyadari bahwa efek samping sering membuat status tidak dapat diserialkan yang tidak dapat disimpan di penyimpanan reaksi, interval, objek dom, permintaan http, dll. saga, rxjs, baconjs, dll. sempurna untuk status kontrol eksternal yang tidak dapat diserialisasi ini.

Saya belum melihat siapa Anda.

Saya pikir reduksi sangat bagus untuk mengekspresikan transisi status tetapi menjadi sangat aneh ketika Anda mengubahnya menjadi mesin status.

Saya setuju. Jika Anda menulis tangan mesin negara yang kompleks, kami memiliki masalah. (Sebenarnya akan lebih rapi jika kita bisa mengubah generator menjadi peredam).

Tetapi saya bertanya-tanya apakah masalah ini dapat dibuat transparan, dalam praktiknya, jika jenis efek yang berbeda dapat dibungkus dalam "driver" yang berbeda? Saya membayangkan cukup mudah bagi orang untuk memilih driver yang mereka inginkan atau menulis sendiri untuk efek baru.

Saya tidak yakin apa yang Anda pikirkan di sini. Saya dapat melihat driver yang berbeda melakukan hal-hal bermanfaat yang berbeda, tetapi menghilangkan status kontrol?

@winstonewert tidak, saya tidak menganggap apa pun sebagai serangan. Saya bahkan tidak punya waktu untuk benar-benar melihat kode Anda :)

Saya tidak begitu mengerti maksud Anda di sini. Anda sepertinya berargumen bahwa saga adalah proyeksi dari log peristiwa? Tapi tidak. Jika saya memutar ulang tindakan, saya tidak akan sampai ke tempat yang sama di saga jika saga bergantung pada peristiwa asinkron. Bagi saya tampaknya tak terhindarkan bahwa saga menghasilkan status yang tidak ada di penyimpanan status redux atau proyeksi log peristiwa.

Tidak, bukan, toko redux adalah proyeksi, tetapi kisahnya adalah pendengar sederhana yang lama.

Kisah (juga disebut manajer proses) bukanlah konsep baru, ini berasal dari dunia CQRS dan telah banyak digunakan pada sistem backend di masa lalu.

Kisah ini bukan proyeksi log peristiwa ke struktur data, ini adalah bagian dari orkestrasi yang dapat mendengarkan apa yang terjadi di sistem Anda dan memancarkan reaksi, sisanya adalah detail implementasi. Umumnya saga mendengarkan log peristiwa (dan mungkin hal-hal eksternal lainnya, seperti waktu ...) dan dapat menghasilkan perintah/peristiwa baru. Juga ketika Anda memutar ulang acara di sistem backend Anda biasanya menonaktifkan menonaktifkan efek samping yang dipicu oleh saga.

Perbedaannya adalah bahwa dalam sistem backend, kisah sering kali benar-benar merupakan proyeksi log peristiwa: untuk mengubah statusnya, ia harus memancarkan peristiwa dan mendengarkannya sendiri. Dalam redux-saga seperti yang saat ini diterapkan, akan lebih sulit untuk memutar ulang log peristiwa untuk memulihkan status saga.

Saya tidak yakin apa yang Anda pikirkan di sini. Saya dapat melihat driver yang berbeda melakukan hal-hal bermanfaat yang berbeda, tetapi menghilangkan status kontrol?

Nah, bukan menghilangkannya, hanya menjadikannya sebagai masalah implementasi yang tersembunyi, untuk sebagian besar tujuan.

Tampaknya bagi saya bahwa ada konsensus yang sangat kuat di komunitas Redux bahwa menyimpan status domain di toko adalah kemenangan besar (jika tidak, mengapa Anda menggunakan Redux sama sekali?). Agak kurang konsensus bahwa menyimpan status UI adalah kemenangan, dibandingkan dengan membuatnya dienkapsulasi dalam komponen. Lalu ada ide untuk menyinkronkan status browser di toko, seperti URL (redux-simple-router) atau data formulir. Tapi ini tampaknya menjadi batas terakhir, menyimpan status/tahap proses yang berjalan lama di toko.

Maaf jika ini bersinggungan, tetapi saya pikir pendekatan yang sangat umum dengan kegunaan pengembang yang baik harus memiliki fitur berikut:

  • Jadikan pengguna biasa tidak terlalu peduli dengan bentuk bagaimana efek direpresentasikan di toko. Mereka harus berinteraksi dengan API sederhana, yang abstrak di atas detail itu.
  • Buatlah agar efeknya mudah dikomposisi. Seharusnya terasa alami untuk melakukan hal-hal seperti aliran kontrol, dan efek yang bergantung pada efek lainnya. Ini, tentu saja, adalah di mana abstraksi generator benar-benar bersinar. Ini berfungsi dengan baik dengan sebagian besar aliran kontrol, penutupan menjadi pengecualian. Tetapi mudah untuk melihat betapa rumitnya aliran asinkron dapat diekspresikan dalam redux-saga atau react-redux-controller.
  • Buat agar status efek dapat dengan mudah ditampilkan ke konsumen toko lain, bila diinginkan. Ini akan memungkinkan Anda melakukan hal-hal seperti menyajikan status proses multi-efek kepada pengguna.
  • Mungkin ini sudah jelas, tetapi setiap subsistem yang merangkum status menyinkronkan status itu dengan Redux, dengan mengirimkan tindakan.

Untuk poin kedua itu, saya pikir harus ada sesuatu yang sangat mirip dengan redux-saga. Ini mungkin mendekati apa yang saya pikirkan dengan pembungkus call . Tapi sebuah saga harus "fast-forwardable", dalam arti, untuk membiarkan Anda deserialize ke keadaan perantara.

Ini semua agak sulit, tetapi secara praktis, saya pikir jika ada kemenangan besar yang bisa didapat dengan memiliki satu catatan tindakan terpusat yang dapat diserialkan, melacak keadaan seluruh aplikasi pada tingkat yang sangat terperinci, ini akan menjadi cara untuk memanfaatkannya. Dan saya pikir mungkin memang ada kemenangan besar di luar sana. Saya membayangkan cara yang lebih sederhana untuk melengkapi aplikasi dengan analisis pengguna dan kinerja. Saya membayangkan kemampuan uji yang benar-benar luar biasa, di mana subsistem yang berbeda digabungkan hanya melalui status.

Saya mungkin telah menyimpang jauh sekarang, jadi saya akan berhenti di situ :)

@acjay Saya pikir kami setuju dengan Anda dalam hal ini, masalahnya adalah menemukan implementasi ini yang menyelesaikan semua itu dengan benar :)

Tetapi tampaknya sulit untuk keduanya memiliki api ekspresif dengan generator, dan kemungkinan untuk perjalanan waktu dan keadaan snapshot/pemulihan ... Mungkin dimungkinkan untuk memoize eksekusi efek sehingga kita dapat dengan mudah memulihkan keadaan generator ...

Tidak yakin, tapi ini mungkin menghalangi kisah gaya while(true) { ... } . Akankah pengulangan hanya menjadi konsekuensi dari kemajuan negara?

@acjay @slorber

Seperti yang saya jelaskan di (https://github.com/yelouafi/redux-saga/issues/22#issuecomment-168872101) Perjalanan waktu sendirian (yaitu tanpa hot reload) dimungkinkan untuk saga. Yang Anda perlukan untuk membawa saga ke titik tertentu adalah urutan efek yang dihasilkan dari awal hingga titik itu, serta hasilnya (menyelesaikan atau menolak). Kemudian Anda tinggal menggerakkan generator dengan urutan itu

Di cabang master yang sebenarnya (belum dirilis pada npm). Saga mendukung pemantauan, mereka mengirimkan semua efek yang dihasilkan, serta hasilnya sebagai tindakan ke toko; mereka juga memberikan informasi hierarki untuk melacak grafik aliran kontrol.

Log efek itu dapat dieksploitasi untuk memutar ulang Saga hingga titik tertentu: tidak perlu membuat panggilan api yang sebenarnya karena log sudah berisi tanggapan sebelumnya.

Dalam contoh repo, ada contoh monitor saga (diimplementasikan sebagai middleware Redux). Itu mendengarkan log efek dan mempertahankan struktur pohon internal (dibangun dengan baik dengan malas). Anda dapat mencetak jejak aliran dengan mengirimkan tindakan {type: 'LOG_EFFECT'} ke toko

Berikut adalah tangkapan log efek dari contoh async

saga-log-async

Sunting: maaf tautan gambar diperbaiki

Membuat penasaran! Dan gambar alat dev itu _awesome_.

Itu keren :)

Memang, monitor saga itu cukup keren.

Memikirkannya, bagi saya sepertinya saga memecahkan dua masalah. Pertama, menangani efek asinkron. Kedua, ini menangani interaksi keadaan kompleks yang jika tidak akan membutuhkan mesin keadaan tulisan tangan yang menjengkelkan dalam peredam.

Pendekatan saya hanya menangani masalah pertama. Saya belum menemukan kebutuhan untuk masalah kedua. Mungkin saya belum cukup menulis kode redux untuk menjalankannya.

Ya, tapi saya ingin tahu apakah ada cara untuk menggabungkan kedua ide tersebut. redux-saga's call wrapper adalah tingkat tipuan yang cukup sederhana atas suatu efek, tetapi seandainya Anda dapat menginisialisasi middleware dengan driver untuk berbagai jenis efek, Anda dapat mewakili efek tersebut sebagai data JSONable, dipisahkan dari fungsi itu benar-benar dipanggil. Pengemudi akan menangani detail pengiriman perubahan status yang mendasarinya ke toko.

Itu mungkin menjadi banyak kerumitan tambahan untuk sedikit manfaat praktis. Tapi hanya mencoba mengikuti garis pemikiran ini sampai akhir.

Oke, saya telah mengumpulkan lebih banyak perpustakaan dan mem-porting contoh dunia nyata untuk menggunakannya:

Pertama, kami memiliki implementasi reaksi:
https://github.com/winstonewert/redux-reactions/blob/master/src/index.js
Antarmuka terdiri dari tiga fungsi: startReactions mengambil penyimpanan, fungsi reaksi, dan pemetaan dari nama ke driver. fromEmitter dan fromPromiseFactory keduanya membuat driver.

Di sini contoh memanggil startReactions untuk mengaktifkan sistem:
https://github.com/winstonewert/redux-reactions/blob/master/examples/real-world/store/configureStore.dev.js#L28

Konfigurasi dasar reaksi di sini:
https://github.com/winstonewert/redux-reactions/blob/master/examples/real-world/reactions/index.js.
Fungsi reaksi sebenarnya hanya beralih melalui komponen yang bereaksi router instantiates mencari yang dengan fungsi reaksi() untuk mengetahui reaksi yang sebenarnya diperlukan untuk halaman itu.

Implementasi tipe reaksi github api ada di sini: https://github.com/winstonewert/redux-reactions/blob/master/examples/real-world/reactions/api.js. Ini sebagian besar salin/tempel dari middleware yang digunakan dalam contoh asli. Titik kritisnya ada di sini: https://github.com/winstonewert/redux-reactions/blob/master/examples/real-world/reactions/api.js#L79 , di mana ia menggunakan fromPromiseFactory untuk membuat driver dari fungsi yang mengembalikan janji.

Lihat fungsi reaksi spesifik komponen di sini: https://github.com/winstonewert/redux-reactions/blob/master/examples/real-world/containers/RepoPage.js#L80.

Pembuat reaksi dan logika umum ada di https://github.com/winstonewert/redux-reactions/blob/master/examples/real-world/reactions/data.js

Halo teman-teman! Raise baru saja menerbitkan sebuah toko enhancer yang memungkinkan Anda menggunakan sistem efek seperti arsitektur Elm juga! Saya harap kita dapat mempelajari dan meningkatkan semua pendekatan ini ke depan untuk memenuhi semua kebutuhan masyarakat :senyum:

https://github.com/raisemarketplace/redux-loop

Siapa pun yang tertarik dengan diskusi mungkin ingin melihat diskusi lebih lanjut tentang ide saya di sini: https://github.com/winstonewert/redux-reactions/issues/7

Anda juga dapat melihat cabang di sini, tempat saya mengerjakan ulang aplikasi penghitung menjadi lebih elmish menggunakan pola saya:
https://github.com/winstonewert/redux-reactions/tree/elmish/examples/counter

Saya juga menemukan bahwa saya menemukan kembali pendekatan yang digunakan di sini: https://github.com/ccorcos/elmish

Hai @yelouafi , bisakah Anda memposting ulang tautan ke ide monitor saga? Itu adalah beberapa hal yang sangat bagus! Tautannya tampaknya mati (404). Saya ingin melihat lebih banyak!

(Saya yakin ini terkait. Maaf jika itu salah tempat)

Bisakah kita memperlakukan semua efek sama dengan rendering DOM?

  1. jQuery adalah driver DOM dengan antarmuka imperatif. React adalah driver DOM dengan antarmuka deklaratif. Jadi, alih-alih memesan: "nonaktifkan tombol itu", kami menyatakan: "kami membutuhkan tombol itu dinonaktifkan" dan pengemudi memutuskan manipulasi DOM apa yang harus dilakukan. Alih-alih memesan: " GET \product\123 ", kami menyatakan: "kami membutuhkan data itu" dan pengemudi memutuskan permintaan apa yang akan dikirim/dibatalkan.
  2. Kami menggunakan komponen React sebagai driver API ke DOM. Mari kita gunakan untuk antarmuka ke driver lain juga.

    • <button ...> - kita membangun lapisan View kita dari komponen React "normal"

    • <Map ...> - kami menggunakan komponen "wrapper" untuk mengubah antarmuka imperatif dari beberapa pustaka menjadi antarmuka deklaratif. Kami menggunakannya dengan cara yang sama seperti komponen "normal", tetapi secara internal sebenarnya adalah driver.

    • <Chart ...> - ini bisa berupa salah satu di atas tergantung pada implementasinya. Jadi, garis antara komponen "normal" dan driver sudah kabur.

    • <Http url={'/product/'+props.selectedProductId} onSuccess={props.PRODUCT_LOADED} /> (atau "pintar" <Service...> ) - kami membangun komponen driver lapisan Layanan kami (tanpa UI)

Layer View dan Service dideskripsikan melalui komponen React. Dan komponen tingkat atas (terhubung) kami merekatkan bersama.
Dengan cara ini reduksi kami tetap murni dan kami tidak memperkenalkan cara baru untuk menangani efek.

Tidak yakin bagaimana new Date atau Math.random cocok di sini.

Apakah selalu memungkinkan untuk mengonversi API imperatif menjadi deklaratif?
Apakah menurut Anda ini adalah pandangan yang layak sama sekali?

Terima kasih

Mengingat kami memiliki kisah dan alat luar biasa lainnya untuk tindakan asinkron, saya pikir kami dapat menutup ini dengan aman sekarang. Lihat #1528 untuk beberapa arah baru yang menarik (selain asinkron juga).

Apakah halaman ini membantu?
0 / 5 - 0 peringkat