redux-saga

匿名 (未验证) 提交于 2019-12-02 23:57:01

redux-sage和redux-thunk类似都是redux的中间件,都用于处理异步操作。redux-saga使用ES6的Generator功能,避免了redux-thunk的回调写法,并且便于测试。

下面展示了最简单是使用示例

import { call, put, takeEvery, takeLatest } from 'redux-saga/effects' import Api from '...'  // worker Saga : 将在 USER_FETCH_REQUESTED action 被 dispatch 时调用 function* fetchUser(action) {    try {       const user = yield call(Api.fetchUser, action.payload.userId);       yield put({type: "USER_FETCH_SUCCEEDED", user: user});    } catch (e) {       yield put({type: "USER_FETCH_FAILED", message: e.message});    } }  /*   在每个 `USER_FETCH_REQUESTED` action 被 dispatch 时调用 fetchUser   允许并发(译注:即同时处理多个相同的 action) */ function* mySaga() {   yield takeEvery("USER_FETCH_REQUESTED", fetchUser); }  /*   也可以使用 takeLatest    不允许并发,dispatch 一个 `USER_FETCH_REQUESTED` action 时,   如果在这之前已经有一个 `USER_FETCH_REQUESTED` action 在处理中,   那么处理中的 action 会被取消,只会执行当前的 */ function* mySaga() {   yield takeLatest("USER_FETCH_REQUESTED", fetchUser); }  export default mySaga;
import { createStore, applyMiddleware } from 'redux' import createSagaMiddleware from 'redux-saga'  import reducer from './reducers' import mySaga from './sagas'  // create the saga middleware const sagaMiddleware = createSagaMiddleware() // mount it on the Store const store = createStore(   reducer,   applyMiddleware(sagaMiddleware) )  // then run the saga sagaMiddleware.run(mySaga)  // render the application

put等一些方法是saga提供的指令,返回一个Effect,Effect是一个简单的js对象,包含了要被sgag middleware执行的指令,当middleware拿到一个被saga yield的Effect,它会暂停saga,直到Effect执行完成,然后saga回恢复执行。

redux-saga提供一些辅助函数,用于在action被发起到store是派生人物。

takeEvery()可以在某个action发起是启动一个任务

import { takeEvery } from 'redux-saga'  function* watchFetchData() {   yield* takeEvery('FETCH_REQUESTED', fetchData) }

takeLatest()和takeEvent()功能类似,不同的是takeEvent()允许同时启动多个任务,即使已启动的任务未结束,也会启动一个新的任务。takeLatest()同时只允许启动一个任务,一个新的任务启动,之前的任务即使未完成也会被取消。

如果有多个sage监听不同的action

import { takeEvery } from 'redux-saga/effects'  // FETCH_USERS function* fetchUsers(action) { ... }  // CREATE_USER function* createUser(action) { ... }  // 同时使用它们 export default function* rootSaga() {   yield takeEvery('FETCH_USERS', fetchUsers)   yield takeEvery('CREATE_USER', createUser) }

在 Generator 函数中,yield

测试时我我们不可能真正的执行任务发到服务器,我们需要检测的只是yield后面语句调用(通常是调用参数的检查)或值是否正确。但当yield 一个返回值是Promise的方法时,就没办法比较了,所以当yield一个异步api时使用saga提供的call()方法代替异步api的直接调用,call()返回一个普通的js对象,所以在测试时可以很容易检测。

import { call } from 'redux-saga/effects'  function* fetchProducts() {   const products = yield call(Api.fetch, '/products')   // ... }
import { call } from 'redux-saga/effects' import Api from '...'  const iterator = fetchProducts()  // expects a call instruction assert.deepEqual(   iterator.next().value,   call(Api.fetch, '/products'),   "fetchProducts should yield an Effect call(Api.fetch, './products')" )

当dispatch一个action时,也同样难以测试,所以saga提供了put()方法来代替直接调用dispatch。

import { call, put } from 'redux-saga/effects' //...  function* fetchProducts() {   const products = yield call(Api.fetch, '/products')   // 创建并 yield 一个 dispatch Effect   yield put({ type: 'PRODUCTS_RECEIVED', products }) }
import { call, put } from 'redux-saga/effects' import Api from '...'  const iterator = fetchProducts()  // 期望一个 call 指令 assert.deepEqual(   iterator.next().value,   call(Api.fetch, '/products'),   "fetchProducts should yield an Effect call(Api.fetch, './products')" )  // 创建一个假的响应对象 const products = {}  // 期望一个 dispatch 指令 assert.deepEqual(   iterator.next(products).value,   put({ type: 'PRODUCTS_RECEIVED', products }),   "fetchProducts should yield an Effect put({ type: 'PRODUCTS_RECEIVED', products })" )

错误处理

使用try...catch来处理错误

import Api from './path/to/api' import { call, put } from 'redux-saga/effects'  // ...  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 })   } }

在测试时使用throw来抛出一个错误

import { call, put } from 'redux-saga/effects' import Api from '...'  const iterator = fetchProducts()  // 期望一个 call 指令 assert.deepEqual(   iterator.next().value,   call(Api.fetch, '/products'),   "fetchProducts should yield an Effect call(Api.fetch, './products')" )  // 创建一个模拟的 error 对象 const error = {}  // 期望一个 dispatch 指令 assert.deepEqual(   iterator.throw(error).value,   put({ type: 'PRODUCTS_REQUEST_FAILED', error }),   "fetchProducts should yield an Effect put({ type: 'PRODUCTS_REQUEST_FAILED', error })" )

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!