初识Redux-Saga
Redus-saga是一个redux的中间件,主要用来简便而优雅的处理redux应用里的副作用(side effect相对于pure function这类概念而言的)。它之所以可以做到这一点主要是使用了ES6里的一个语法:Generator。使用Generator可以像写同步的代码一样编写异步代码,这样更加容易测试。
在我们更深入之前,“saga”这个名字在计算机科学的历史上早已存在,并不是只用于javascript里的。Saga可以简要的概括为一种处理长时间运行的事务,并且这种事务会有副作用或者失败的可能。每一个我们希望完成的事务,都需要一个反事务在出错的时候把事务恢复到当前事务发生之前的样子。如果有兴趣了解更多Sage,我(作者)推荐你看看Caitie McCaffrey的这段演讲,演讲的题目是《实践Saga模式》。另外可以参阅Roman Liutikov的博客,叫做《Saga模式的迷惑之处》。
我们为什么要用Redux-saga
现在我们可以创建一个新的react-redux应用,我们会使用redux-thunk和redux-saga处理异步的action。所以我们为什么要用redux-saga呢?
正如文档里所说:
与redux-thunk相反,你不会跌入回调地狱,你可以很容易的测试你的异步流程而且action一直保持pure(纯的状态)。
我们来对比一下saga和thunk的用法来更深入的理解上面那句话的内容。假设场景为:用户点击按钮,发出一个http请求来获取数据。
Redux thunk的写法:
import {
API_BUTTON_CLICK,
API_BUTTON_CLICK_SUCCESS,
API_BUTTON_CLICK_ERROR,
} from './actions/consts';
import { getDataFromAPI } from './api';
const getDataStarted = () => ({type: API_BUTTON_CLICK});
const getDataSuccess = data => ({type: API_BUTTON_CLICK_SUCESS, payload: data});
const getDataError = message => ({type: API_BUTTON_CLICK_ERROR, payload: message});
const getDataFromAPI = () => {
return dispatch => {
dispatch(getDataStarted());
getDataFromAPI()
.then(data => {
dispatch(getUserSucess(data));
}).fail(err => {
dispatch(getDataError(err.message));
})
}
}
这里我们有一个叫做getDataFromAPI()的方法来创建action。当用户点击按钮开始了异步流程的时候“
- 首先触发一个action让我们的store知道我们要发起一个异步请求(
dispatch(getDataStarted())了。 - 接着我们实际发出了API请求,这个请求会返回一个promise。
- 然后我们会在成功接受到请求数据的时候触发成功的action,有错误的时候触发错误的action。
Redux saga的写法:
import { call, put, takeEvery } from 'redux-saga/effects';
import {
API_BUTTON_CLICK,
API_BUTTON_CLICK_SUCCESS,
API_BUTTON_CLICK_ERROR,
} from './actoins/consts';
import { getDataFromAPI } from './api';
export function* apiSideEffect(action) {
try{
const data = yield call(getDataFromaAPI);
yield put({ type: API_BUTTON_CLICK_SUCESS, payload: data});
} catch(e) {
yield put({type: API_BUTTON_CLICK_ERROR, payload: e.message});
}
}
// the 'watcher' -on every `API_BUTTON_CLICK` action, run our side effect
export function* apiSaga() {
yield takeEvery(API_BUTTON_CLICK, apiSideEffect);
}
流程基本上都是一样的,只是代码看起来略有不同:
- 与thunk例子不同的是,我们没有使用
dispatch而是用了put(我们可以认为两者是等价的)。 - 我们有一个监听方法一直监听着“start”方法。它只会在按钮点击之后触发一个类型为
API_BUTTON_CLICK的redux action。 - 我们用redux-saga的
calleffect(专有名词,效果的意思。与side effect的effect同意)来从异步的方法(promise,不同的saga等)里面获取数据。
非常简单,对吧。在某些按钮的点击事件里,我们会请求某些终端来获取数据。如果成功了,就发起一个新的,payload就是数据的action。否则就发出一个带有error消息的action。
同时,需要注意认真的理解上面的例子非常重要。不是说thunk的例子难以理解,但是我们却摆脱了返回方法或者promise链的麻烦。我们还可以只简单用一个try-catch来处理任何的异步错误。然后put(或者dispatch)一个action来通知reducer。
其次,更重要的是,我们的saga side effect(saga副作用)是纯的(pure)。这是因为call(getDataFromAPI)并不实际执行API请求,它只是返回一个纯对象:{type: 'CALL', func, args}。实际的请求已经由redux-saga中间件执行,并且会把返回值带到generator里(所以需要用yeild关键字)或者抛出一个异常,如果有的话。
掌握了以上概念以后,你就会明白下面的测试为什么这么简单:
import { call, put } from 'redux-saga/effects';
import { API_BUTTON_CLICK_SUCCESS, } from './actions/consts';
import { getDataFromAPI } from './api';
it('apiSideEffect - fetches data from API and dispatches a success action', () => {
const generator = apiSideEffect();
expect(generator.next().value).toEqual(call(getDataFromAPI));
expect(generator.next().value).toEqual(put({type: API_BUTTON_CLICK_SUCCESS }));
expect(generator.next()).toEqual({done: true, value: undefined});
});
然而上面的测试代码有一点点小问题。我们需要模拟getDataFromAPI方法的调用,其他在上面语句块的方法也需要模拟出来。这也许不是什么大的工作量,但是随着我们的action数量的增长,复杂度也会增加,类似上面的测试最好避免。
本文希望讲清楚redux-saga三个要点。我们不会再遇到回调地狱,我们的action是纯的,而且我们的异步流程很容易测试。如果你要学到更多,下面的资源会有帮助:
原文地址:https://engineering.universe.com/what-is-redux-saga-c1252fc2f4d1
初识Redux-Saga的更多相关文章
- [React] 14 - Redux: Redux Saga
Ref: Build Real App with React #14: Redux Saga Ref: 聊一聊 redux 异步流之 redux-saga [入门] Ref: 从redux-thun ...
- react系列(六)Redux Saga
在Redux中常要管理异步操作,目前社区流行的有Redux-Saga.Redux-thunk等.在管理复杂应用时,推荐使用Redux-Saga,它提供了用 generator 书写类同步代码的能力. ...
- react native redux saga增加日志功能
redux-logger地址:https://github.com/evgenyrodionov/redux-logger 目前Reac native项目中已经使用redux功能,异步中间件使用red ...
- redux+saga+reducer
saga.js这个文件里面的函数实际没有在其他jsx中引用吧?这个文件的作用就是把异步数据拿到,放进reducer,如果jsx想取,需要结合connect来取数据.
- 初识redux走向redux-react
废话不多说,先上一张图 首先记住redux设计思想 Web应用是一个转态机,视图与转态是一一对应的 所有的转态,保存在一个对象里 1.store 存储数据的容器,整个应用只有一个state,Redux ...
- 初识Redux Middleware
前言 原先改变store是通过dispatch(action) = > reducer:那Redux的Middleware是什么呢?就是dispatch(action) = > reduc ...
- redux saga学习
来源地址:https://www.youtube.com/watch?v=o3A9EvMspig Saga的基本写法 takeEvery与takeLatest的区别 takeEvery是指响应每一个请 ...
- [转] How to dispatch a Redux action with a timeout?
How to dispatch a Redux action with a timeout? Q I have an action that updates notification state of ...
- 《React与Redux开发实例精解》读书笔记
第五章 JSX语法 class属性改为className for属性改为htmlFor jsx中javascript表达式必须要有返回值,使用三元操作符 所有的标签必须闭合 input img等 re ...
随机推荐
- jQuery点击按钮实现div的隐藏和显示切换效果
<script type="text/javascript"> $(function(){ $('#click_event').click(function(){ i ...
- ubuntu中切换到root账号方法
ubuntu如何切换到root用户身份? 前面小编分享了ubuntu怎么开启root用户,下面小编来分享如何切到到root用户 方法/步骤 按照下面的方式打开终端工具,或者使用终端工具的快捷键Ctrl ...
- Vue实现勾选后向数组都添加
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...
- c++ 11 移动语义、std::move 左值、右值、将亡值、纯右值、右值引用
为什么要用移动语义 先看看下面的代码 // rvalue_reference.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #includ ...
- c#中字节数组byte[]、图片image、流stream,字符串string、内存流MemoryStream、文件file,之间的转换
字节数组byte[]与图片image之间的转化 字节数组转换成图片 public static Image byte2img(byte[] buffer) { MemoryStream ms = ne ...
- 为什么阿里的程序员那么帅?---原来他们都有"编码规约扫描"神器在手
为了迎接十九大的到来,帝都城这几天也是满城风雨,听说早高峰期地铁站的人都排到天桥上了,哎,这就是该死的北漂生活.但是无论怎样,我依然在北京向各位问好! 之前总结过俩篇关于阿里Java开发手册的编程规约 ...
- 彻底理解Java的Future模式
先上一个场景:假如你突然想做饭,但是没有厨具,也没有食材.网上购买厨具比较方便,食材去超市买更放心. 实现分析:在快递员送厨具的期间,我们肯定不会闲着,可以去超市买食材.所以,在主线程里面另起一个子线 ...
- Android基础知识02—安卓日志工具LogCat的五种方法
--------Android 02-------- >>> Android的日志工具LogCat 五个方法,记录信息的级别不一样,从低到高为: 1.Log.v()-日志 ...
- Cosmos OpenSSD架构分析--FSC
接口速度: type bw read 75μs 1s/75μs*8k/1s=104m/s write 1300μs 1s/1300μs*8k/1s=6m/s erase 3.8ms 1s/ ...
- 关于python如果没有numpy模块如何处理
1.在python中,你在python的shell输入>>>import numpy 但是编译器告诉你没有numpy库,这时候你就要导入python库,那么如何导入呢 2.收下访问h ...