Redux-saga在Redux应用中扮演’中间件’的角色,主要用来执行数据流中的异步操作。主要通过ES6中的generator函数和yield关键字来以同步的方式实现异步操作。
基本用法:
使用createSagaMiddleware方法创建saga 的Middleware,然后在创建的redux的store时,使用applyMiddleware函数将创建的saga Middleware实例绑定到store上,最后可以调用saga Middleware的run函数来执行某个或者某些Middleware。
在saga的Middleware中,可以使用takeEvery或者takeLatest等API来监听某个action,当某个action触发后,saga可以使用call、fetch等api发起异步操作,操作完成后使用put函数触发action,同步更新state,从而完成整个State的更新。
takeEvery
用来监听action,每个action都触发一次,如果其对应是异步操作的话,每次都发起异步请求,而不论上次的请求是否返回。
call用来调用异步函数,将异步函数和函数参数作为call函数的参数传入,返回一个js对象。saga引入他的主要作用是方便测试,同时也能让我们的代码更加规范化。
同js原生的call一样,call函数也可以指定this对象,只要把this对象当第一个参数传入call方法就好了
saga同样提供apply函数,作用同call一样,参数形式同js原生apply方法。
function
* fetchProducts() {
const products =
yield
call(Api.fetch,
'/products'
)
import
{ call } from
'redux-saga/effects'
import
Api from
'...'
const iterator = fetchProducts()
assert.deepEqual(
iterator.next().value,
call(Api.fetch,
'/products'
),
"fetchProducts should yield an Effect call(Api.fetch, './products')"
yield
call([obj, obj.method], arg1, arg2, ...)
yield
apply(obj, obj.method, [arg1, arg2, ...])
function
* fetchProducts() {
const products =
yield
call(Api.fetch,
'/products'
)
yield
put({ type:
'PRODUCTS_RECEIVED'
, products })
const products = {}
assert.deepEqual(
iterator.next(products).value,
put({ type:
'PRODUCTS_RECEIVED'
, products }),
"fetchProducts should yield an Effect put({ type: 'PRODUCTS_RECEIVED', products })"
function
* fetchProducts() {
try
{
const products =
yield
call(Api.fetch,
'/products'
)
yield
put({ type:
'PRODUCTS_RECEIVED'
, products })
catch
(error) {
yield
put({ type:
'PRODUCTS_REQUEST_FAILED'
, error })
import
Api from
'./path/to/api'
import
{ call, put } from
'redux-saga/effects'
function
fetchProductsApi() {
return
Api.fetch(
'/products'
)
.then(response => ({ response }))
.
catch
(error => ({ error }))
function
* fetchProducts() {
const { response, error } =
yield
call(fetchProductsApi)
if
(response)
yield
put({ type:
'PRODUCTS_RECEIVED'
, products: response })
yield
put({ type:
'PRODUCTS_REQUEST_FAILED'
, error })
take的表现同takeEvery一样,都是监听某个action,但与takeEvery不同的是,他不是每次action触发的时候都相应,而只是在执行顺序执行到take语句时才会相应action。
当在genetator中使用take语句等待action时,generator被阻塞,等待action被分发,然后继续往下执行。
takeEvery只是监听每个action,然后执行处理函数。对于何时相应action和 如何相应action,takeEvery并没有控制权。
而take则不一样,我们可以在generator函数中决定何时相应一个action,以及一个action被触发后做什么操作。
最大区别:take只有在执行流达到时才会响应对应的action,而takeEvery则一经注册,都会响应action。
import
{ take, put } from
'redux-saga/effects'
function
* watchFirstThreeTodosCreation() {
for
(
let
i = 0; i < 3; i++) {
const action =
yield
take(
'TODO_CREATED'
)
yield
put({type:
'SHOW_CONGRATULATION'
})
import
{ take, call, put, cancelled } from
'redux-saga/effects'
import
Api from
'...'
function
* authorize(user, password) {
try
{
const token =
yield
call(Api.authorize, user, password)
yield
put({type:
'LOGIN_SUCCESS'
, token})
yield
call(Api.storeItem, {token})
return
token
}
catch
(error) {
yield
put({type:
'LOGIN_ERROR'
, error})
} finally {
if
(
yield
cancelled()) {
import
{ all, call } from
'redux-saga/effects'
const [users, repos] =
yield
all([
call(fetch,
'/users'
),
call(fetch,
'/repos'
)
function
* watchStartBackgroundTask() {
while
(
true
) {
yield
take(
'START_BACKGROUND_TASK'
)
yield
race({
task: call(backgroundTask),
cancel: take(
'CANCEL_TASK'
)
import
{ take, actionChannel, call, ... } from
'redux-saga/effects'
function
* watchRequests() {
const requestChan =
yield
actionChannel(
'REQUEST'
)
while
(
true
) {
const {payload} =
yield
take(requestChan)
yield
call(handleRequest, payload)
function
* handleRequest(payload) { ... }