Ada banyak pembicaraan tentang anak terbaru di kota redux sekarang, redux-saga / redux-saga . Ini menggunakan fungsi generator untuk mendengarkan / mengirim tindakan.
Sebelum saya membungkus kepala saya, saya ingin tahu pro / kontra dari menggunakan redux-saga
daripada pendekatan di bawah ini di mana saya menggunakan redux-thunk
dengan async / menunggu.
Sebuah komponen mungkin terlihat seperti ini, mengirimkan tindakan seperti biasa.
import { login } from 'redux/auth';
class LoginForm extends Component {
onClick(e) {
e.preventDefault();
const { user, pass } = this.refs;
this.props.dispatch(login(user.value, pass.value));
}
render() {
return (<div>
<input type="text" ref="user" />
<input type="password" ref="pass" />
<button onClick={::this.onClick}>Sign In</button>
</div>);
}
}
export default connect((state) => ({}))(LoginForm);
Maka tindakan saya terlihat seperti ini:
// auth.js
import request from 'axios';
import { loadUserData } from './user';
// define constants
// define initial state
// export default reducer
export const login = (user, pass) => async (dispatch) => {
try {
dispatch({ type: LOGIN_REQUEST });
let { data } = await request.post('/login', { user, pass });
await dispatch(loadUserData(data.uid));
dispatch({ type: LOGIN_SUCCESS, data });
} catch(error) {
dispatch({ type: LOGIN_ERROR, error });
}
}
// more actions...
// user.js
import request from 'axios';
// define constants
// define initial state
// export default reducer
export const loadUserData = (uid) => async (dispatch) => {
try {
dispatch({ type: USERDATA_REQUEST });
let { data } = await request.get(`/users/${uid}`);
dispatch({ type: USERDATA_SUCCESS, data });
} catch(error) {
dispatch({ type: USERDATA_ERROR, error });
}
}
// more actions...
javascript
reactjs
redux
redux-thunk
redux-saga
hampusohlsson
sumber
sumber
::
sebelum Andathis.onClick
lakukan?this
), aliasthis.onClick = this.onClick.bind(this)
. Bentuk yang lebih panjang biasanya direkomendasikan untuk dilakukan di konstruktor, karena tangan pendek mengikat pada setiap render.bind()
untuk lulusthis
ke fungsi, tapi saya mulai menggunakan() => method()
sekarang.Jawaban:
Dalam redux-saga, yang setara dengan contoh di atas adalah
Hal pertama yang perlu diperhatikan adalah kita memanggil fungsi api menggunakan formulir
yield call(func, ...args)
.call
tidak mengeksekusi efeknya, itu hanya menciptakan objek seperti biasa{type: 'CALL', func, args}
. Eksekusi didelegasikan ke middleware redux-saga yang menangani mengeksekusi fungsi dan melanjutkan generator dengan hasilnya.Keuntungan utama adalah bahwa Anda dapat menguji generator di luar Redux menggunakan pemeriksaan kesetaraan sederhana
Catatan kami mengejek hasil panggilan api hanya dengan menyuntikkan data yang diolok-olok ke dalam
next
metode iterator. Mengejek data lebih mudah daripada mengolok-olok fungsi.Hal kedua yang perlu diperhatikan adalah panggilan untuk
yield take(ACTION)
. Thunks dipanggil oleh pencipta aksi pada setiap aksi baru (misalnyaLOGIN_REQUEST
). yaitu tindakan terus didorong ke thunks, dan thunks tidak memiliki kendali kapan harus berhenti menangani tindakan tersebut.Di redux-saga, generator melakukan aksi selanjutnya. yaitu mereka memiliki kendali kapan harus mendengarkan suatu tindakan, dan kapan tidak. Pada contoh di atas, instruksi aliran ditempatkan di dalam sebuah
while(true)
loop, sehingga akan mendengarkan setiap tindakan yang masuk, yang agak meniru perilaku mendorong thunk.Pendekatan tarikan memungkinkan penerapan aliran kontrol yang kompleks. Misalkan misalnya kita ingin menambahkan persyaratan berikut
Menangani tindakan pengguna LOGOUT
setelah login pertama yang berhasil, server mengembalikan token yang kedaluwarsa dalam penundaan yang disimpan dalam suatu
expires_in
bidang. Kami harus menyegarkan otorisasi di latar belakang pada setiapexpires_in
milidetikMempertimbangkan bahwa ketika menunggu hasil panggilan api (baik login awal atau refresh) pengguna dapat keluar di antaranya.
Bagaimana Anda menerapkannya dengan thunks; sementara juga memberikan cakupan uji penuh untuk seluruh aliran? Berikut ini tampilannya dengan Sagas:
Dalam contoh di atas, kami menyatakan persyaratan concurrency kami menggunakan
race
. Jikatake(LOGOUT)
memenangkan perlombaan (yaitu pengguna mengklik tombol Logout). Perlombaan akan secara otomatis membatalkanauthAndRefreshTokenOnExpiry
tugas latar belakang. Dan jikaauthAndRefreshTokenOnExpiry
diblokir di tengahcall(authorize, {token})
panggilan, itu juga akan dibatalkan. Pembatalan menyebar ke bawah secara otomatis.Anda dapat menemukan demo runnable dari aliran di atas
sumber
delay
fungsi itu berasal? Ah, menemukannya: github.com/yelouafi/redux-saga/blob/…redux-thunk
kode cukup dibaca dan self-menjelaskan. Tapiredux-sagas
satu ini benar-benar dibaca, terutama karena mereka verba-seperti fungsi:call
,fork
,take
,put
...Saya akan menambahkan pengalaman saya menggunakan saga dalam sistem produksi selain jawaban penulis perpustakaan yang agak menyeluruh.
Pro (menggunakan saga):
Testabilitas. Sangat mudah untuk menguji kisah sebagai panggilan () mengembalikan objek murni. Menguji pukulan biasanya mengharuskan Anda untuk memasukkan mockStore di dalam pengujian Anda.
redux-saga hadir dengan banyak fungsi pembantu yang berguna tentang tugas. Menurut saya konsep saga adalah menciptakan semacam pekerja latar belakang / utas untuk aplikasi Anda, yang bertindak sebagai bagian yang hilang dalam arsitektur redux reaksi (actionCreator dan reduksi harus merupakan fungsi murni.) Yang mengarah ke poin berikutnya.
Sagas menawarkan tempat independen untuk menangani semua efek samping. Biasanya lebih mudah untuk memodifikasi dan mengelola daripada tindakan thunk dalam pengalaman saya.
Menipu:
Sintaks generator.
Banyak konsep untuk dipelajari.
Stabilitas API. Tampaknya redux-saga masih menambahkan fitur (mis. Saluran?) Dan komunitasnya tidak sebesar. Ada kekhawatiran jika perpustakaan membuat pembaruan yang tidak kompatibel ke belakang suatu hari nanti.
sumber
API stability
sebagai pembaruan untuk mencerminkan situasi saat ini.Saya hanya ingin menambahkan beberapa komentar dari pengalaman pribadi saya (menggunakan kedua kisah dan thunk):
Sagas bagus untuk diuji:
Sagas lebih kuat. Semua yang dapat Anda lakukan di pencipta aksi satu thunk Anda juga dapat melakukannya dalam satu saga, tetapi tidak sebaliknya (atau setidaknya tidak mudah). Sebagai contoh:
take
)cancel
,takeLatest
,race
)take
,takeEvery
, ...)Sagas juga menawarkan fungsionalitas bermanfaat lainnya, yang menggeneralisasi beberapa pola aplikasi umum:
channels
untuk mendengarkan sumber acara eksternal (mis. websockets)fork
,spawn
)Sagas adalah alat yang hebat dan kuat. Namun dengan kekuatan datang tanggung jawab. Ketika aplikasi Anda tumbuh, Anda bisa dengan mudah tersesat dengan mencari tahu siapa yang menunggu tindakan untuk dikirim, atau apa yang semuanya terjadi ketika beberapa tindakan sedang dikirim. Di sisi lain thunk lebih sederhana dan lebih mudah untuk dipikirkan. Memilih satu atau yang lain tergantung pada banyak aspek seperti jenis dan ukuran proyek, jenis efek samping apa yang harus ditangani oleh proyek Anda atau pilih preferensi tim. Dalam hal apa pun, jaga agar aplikasi Anda tetap sederhana dan dapat diprediksi.
sumber
Hanya beberapa pengalaman pribadi:
Untuk gaya pengkodean dan keterbacaan, salah satu keuntungan paling signifikan dari menggunakan redux-saga di masa lalu adalah untuk menghindari panggilan balik neraka di redux-thunk - kita tidak perlu menggunakan banyak sarang kemudian / tangkap lagi. Tetapi sekarang dengan popularitas async / wait thunk, kita juga bisa menulis kode async dengan gaya sinkronisasi ketika menggunakan redux-thunk, yang dapat dianggap sebagai peningkatan dalam redux-think.
Orang mungkin perlu menulis kode boilerplate lebih banyak ketika menggunakan redux-saga, terutama di Typecript. Sebagai contoh, jika seseorang ingin mengimplementasikan fungsi ambil async, penanganan data dan kesalahan dapat langsung dilakukan dalam satu unit thunk dalam action.js dengan satu tindakan FETCH. Tetapi dalam redux-saga, seseorang mungkin perlu mendefinisikan FETCH_START, FETCH_SUCCESS, dan FETCH_FAILURE tindakan dan semua pemeriksaan jenisnya yang terkait, karena salah satu fitur di redux-saga adalah menggunakan mekanisme "token" kaya ini untuk membuat efek dan menginstruksikan toko redux untuk pengujian mudah. Tentu saja orang bisa menulis saga tanpa menggunakan tindakan ini, tetapi itu akan membuatnya mirip dengan seorang pencuri.
Dalam hal struktur file, redux-saga tampaknya lebih eksplisit dalam banyak kasus. Seseorang dapat dengan mudah menemukan kode terkait async di setiap sagas.ts, tetapi dalam redux-thunk, orang perlu melihatnya dalam tindakan.
Pengujian mudah dapat menjadi fitur lain dalam redux-saga. Ini benar-benar nyaman. Tetapi satu hal yang perlu diklarifikasi adalah bahwa uji “panggilan” redux-saga tidak akan melakukan panggilan API yang sebenarnya dalam pengujian, sehingga orang perlu menentukan hasil sampel untuk langkah-langkah yang mungkin menggunakannya setelah panggilan API. Karena itu sebelum menulis di redux-saga, akan lebih baik untuk merencanakan kisah dan sagas.spec.ts yang sesuai secara rinci.
Redux-saga juga menyediakan banyak fitur canggih seperti menjalankan tugas secara paralel, pembantu konkurensi seperti takeLatest / takeEvery, fork / spawn, yang jauh lebih kuat daripada thunks.
Sebagai kesimpulan, secara pribadi, saya ingin mengatakan: dalam banyak kasus normal dan aplikasi berukuran kecil hingga sedang, gunakan async / await style redux-thunk. Ini akan menghemat banyak kode / tindakan / typedef boilerplate, dan Anda tidak perlu beralih di banyak sagas.ts yang berbeda dan mempertahankan pohon sagas tertentu. Tetapi jika Anda sedang mengembangkan aplikasi besar dengan banyak logika async yang kompleks dan kebutuhan akan fitur-fitur seperti concurrency / pola paralel, atau memiliki permintaan tinggi untuk pengujian dan pemeliharaan (terutama dalam pengembangan yang digerakkan oleh tes), redux-saga mungkin akan menyelamatkan hidup Anda .
Bagaimanapun, redux-saga tidak lebih sulit dan kompleks daripada redux itu sendiri, dan itu tidak memiliki kurva belajar curam karena memiliki konsep inti dan API yang terbatas. Menghabiskan sedikit waktu untuk belajar redux-saga mungkin bermanfaat bagi diri Anda sendiri suatu hari nanti.
sumber
Setelah meninjau beberapa proyek React / Redux skala besar yang berbeda dalam pengalaman saya, Sagas memberikan pengembang cara menulis kode yang lebih terstruktur yang jauh lebih mudah untuk diuji dan sulit untuk mendapatkan kesalahan.
Ya itu agak aneh untuk memulainya, tetapi kebanyakan devs sudah cukup memahaminya dalam sehari. Saya selalu memberi tahu orang-orang untuk tidak khawatir tentang apa yang
yield
harus dimulai dan bahwa begitu Anda menulis beberapa tes, itu akan datang kepada Anda.Saya telah melihat beberapa proyek di mana thunks telah diperlakukan seolah-olah mereka adalah pengontrol dari patten MVC dan ini dengan cepat menjadi kekacauan yang tidak dapat dipecahkan.
Saran saya adalah menggunakan Sagas di mana Anda membutuhkan A memicu jenis barang B yang berkaitan dengan satu peristiwa. Untuk apa pun yang dapat memotong sejumlah tindakan, saya merasa lebih mudah untuk menulis middleware pelanggan dan menggunakan properti meta dari tindakan FSA untuk memicunya.
sumber
Thunks versus Sagas
Redux-Thunk
danRedux-Saga
berbeda dalam beberapa cara penting, keduanya adalah pustaka middleware untuk Redux (muxleware Redux adalah kode yang memotong tindakan yang masuk ke toko melalui metode dispatch ()).Suatu tindakan dapat berupa apa saja, tetapi jika Anda mengikuti praktik terbaik, suatu tindakan adalah objek javascript biasa dengan bidang jenis, dan bidang muatan opsional, meta, dan kesalahan. misalnya
Redux-Thunk
Selain mengirim tindakan standar,
Redux-Thunk
middleware memungkinkan Anda untuk mengirim fungsi khusus, yang disebutthunks
.Thunks (dalam Redux) umumnya memiliki struktur berikut:
Yaitu, a
thunk
adalah fungsi yang (opsional) mengambil beberapa parameter dan mengembalikan fungsi lainnya. Fungsi dalam mengambildispatch function
dangetState
fungsi - yang keduanya akan disediakan olehRedux-Thunk
middleware.Redux-Saga
Redux-Saga
middleware memungkinkan Anda untuk mengekspresikan logika aplikasi yang kompleks sebagai fungsi murni yang disebut saga. Fungsi murni diinginkan dari sudut pandang pengujian karena dapat diprediksi dan berulang, yang membuatnya relatif mudah untuk diuji.Sagas diimplementasikan melalui fungsi khusus yang disebut fungsi generator. Ini adalah fitur baru dari
ES6 JavaScript
. Pada dasarnya, eksekusi melompat masuk dan keluar dari generator di mana pun Anda melihat pernyataan hasil. Pikirkanyield
pernyataan yang menyebabkan generator berhenti dan mengembalikan nilai yang dihasilkan. Kemudian, penelepon dapat melanjutkan generator di pernyataan berikutyield
.Fungsi generator didefinisikan seperti ini. Perhatikan tanda bintang setelah kata kunci fungsi.
Setelah saga login terdaftar
Redux-Saga
. Tetapi kemudianyield
mengambil pada baris pertama akan menghentikan saga sampai tindakan dengan tipe'LOGIN_REQUEST'
dikirim ke toko. Setelah itu terjadi, eksekusi akan berlanjut.Untuk lebih jelasnya lihat artikel ini .
sumber
Satu catatan singkat. Generator dapat dibatalkan, async / menunggu - tidak. Jadi sebagai contoh dari pertanyaan, itu tidak benar-benar masuk akal apa yang harus dipilih. Tetapi untuk aliran yang lebih rumit kadang-kadang tidak ada solusi yang lebih baik daripada menggunakan generator.
Jadi, ide lain adalah menggunakan generator dengan redux-thunk, tetapi bagi saya, sepertinya mencoba menciptakan sepeda dengan roda persegi.
Dan tentu saja, generator lebih mudah untuk diuji.
sumber
Berikut adalah proyek yang menggabungkan bagian (pro) terbaik dari keduanya
redux-saga
danredux-thunk
: Anda dapat menangani semua efek samping pada kisah sambil mendapatkan janji dengandispatching
tindakan yang sesuai: https://github.com/diegohaz/redux-saga-thunksumber
then()
di dalam komponen React bertentangan dengan paradigma. Anda harus menangani kondisi yang diubahcomponentDidUpdate
daripada menunggu janji untuk diselesaikan.componentDidlMount() { this.props.doSomething().then((detail) => { this.setState({isReady: true})} }
Cara yang lebih mudah adalah dengan menggunakan redux-auto .
dari dokumentasi
Idenya adalah memiliki setiap tindakan dalam file tertentu . co-locating panggilan server dalam file dengan fungsi peredam untuk "pending", "terpenuhi" dan "ditolak". Ini membuat penanganan janji sangat mudah.
Itu juga secara otomatis melampirkan objek pembantu (disebut "async") ke prototipe negara Anda, memungkinkan Anda untuk melacak di UI Anda, transisi yang diminta.
sumber