[React] 14 - Redux: Redux Saga
Ref: 聊一聊 redux 异步流之 redux-saga [入门]
Ref: 从redux-thunk到redux-saga实践 [深入]
Ref: Saga中文文档
函数式编程范式的“副作用”
在实际的应用开发中,我们希望做一些异步的(如Ajax请求)且不纯粹的操作(如改变外部的状态),这些在函数式编程范式中被称为“副作用”。
Redux 的作者将这些副作用的处理通过提供中间件的方式让开发者自行选择进行实现。
redux-saga 就是用来处理上述副作用(异步任务)的一个中间件。
它是一个接收事件,并可能触发新事件的过程管理者,为你的应用管理复杂的流程。
参考:[React] 12 - Redux: async & middleware
需求:"假如当每次 Button 被点击的时候,我们想要从给定的 url 中获取数据"
redux-thunk 的主要思想是扩展 action,使得 action 从一个对象变成一个函数。
采用 redux-thunk, 我们会这样写:
// fetchUrl 返回一个 thunk
function fetchUrl(url) {
return (dispatch) => {
dispatch({
type: 'FETCH_REQUEST'
}); fetch(url).then(data => dispatch({
type: 'FETCH_SUCCESS',
data
}));
}
} // 如果 thunk 中间件正在运行的话,我们可以 dispatch 上述函数如下:
dispatch(
fetchUrl(url)
):
redux-thunk 的缺点
(2)thunks 内部测试逻辑比较困难,需要mock所有的触发函数;
(3)协调并发任务比较困难,当自己的 action 调用了别人的 action,别人的 action 发生改动,则需要自己主动修改;
(4)业务逻辑会散布在不同的地方:启动的模块,组件以及thunks内部。
// redux-thunk example
import {applyMiddleware, createStore} from 'redux';
import axios from 'axios';
import thunk from 'redux-thunk'; const initialState = { fetching: false, fetched: false, users: [], error: null }
const reducer = (state = initialState, action) => {
switch(action.type) {
case 'FETCH_USERS_START': {
return {...state, fetching: true}
break;
}
case 'FETCH_USERS_ERROR': {
return {...state, fetching: false, error: action.payload}
break;
}
case 'RECEIVE_USERS': {
return {...state, fetching: false, fetched: true, users: action.payload}
break;
}
}
return state;
}
const middleware = applyMiddleware(thunk); // store.dispatch({type: 'FOO'});
// redux-thunk 的作用即是将 action: object --> function
store.dispatch((dispatch) => {
dispatch({type: 'FETCH_USERS_START'});
// do something async
axios.get('http://rest.learncode.academy/api/wstern/users')
.then((response) => {
dispatch({type: 'RECEIVE_USERS', payload: response.data})
})
.catch((err) => {
dispatch({type: 'FECTH_USERS_ERROR', payload: err})
})
});
Saga来了

yield Effects(包含指令的文本对象)。effects对象的API 例如: fork,call,take,put,cancel 等来创建 Effect。// Effect -> 调用 fetch 函数并传递 `./products` 作为参数
{
type: CALL,
function: fetch,
args: ['./products']
}
第二,
dispatch 一个 action 来通知在 UI 中哪些地方发生了改变,- worker saga
做所有的工作,如调用 API,进行异步请求,并且获得返回结果 - watcher saga
监听被 dispatch 的 actions,当接收到 action 或者知道其被触发时,调用 worker saga 执行任务 - root saga
立即启动 sagas 的唯一入口
在 redux-saga 中的基本概念就是:
call),但是会构造一个需要执行作用的描述。如何使用Saga
1. 加入 saga 中间件,并且启动它,它会一直运行
//...
import { createStore, applyMiddleware} from 'redux';
import createSagaMiddleware from 'redux-saga';
import appReducer from './reducers';
//... const sagaMiddleware = createSagaMiddleware();
const middlewares = [sagaMiddleware]; const store = createStore(appReducer, applyMiddleware(...middlewares));
sagaMiddleware.run(rootSaga); render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('app')
);
2. 在 sagas 文件夹中集中写 saga 文件
import { take, fork, call, put } from 'redux-saga/effects'; // jeff: 都是纯函数,每个函数构造一个特殊的对象
// The worker: perform the requested task
function* fetchUrl(url) {
const data = yield call(fetch, url); // 指示中间件调用 fetch 异步任务 jeff: data是一个类似于 {type: CALL, function: fetchUrl, args: [url]} 的对象
/**
* 中间件会停止 generator 函数,
* 直到 fetch 返回的 Promise 被 resolved(或 rejected)
*/
yield put({ type: 'FETCH_SUCCESS', data }); // 指示中间件发起一个 action 到 Store
}
------------------------------------------------------------
// The watcher: watch actions and coordinate worker tasks
function* watchFetchRequests() {
while(true) {
const action = yield take('FETCH_REQUEST'); // 指示中间件等待 Store 上指定的 action,即监听 action
/**
* 中间件会暂停执行 wacthFetchRequests generator 函数,
* 直到 FETCH_REQUEST action 被 dispatch。
*/
yield fork(fetchUrl, action.url); // 指示中间件以无阻塞调用方式执行 fetchUrl
/**
* 告诉中间件去无阻塞调用一个新的 fetchUrl 任务,
* action.url 作为 fetchUrl 函数的参数传递。
*/
}
}
总结领悟:
JavaScript 是单线程的,redux-saga 让事情看起来是同时进行的。
架构上的优势:
将所有的异步流程控制都移入到了 sagas,UI 组件不用执行业务逻辑,只需 dispatch action 就行,增强组件复用性。
结合具体使用场景:登录
export function* loginSaga() {
while(true) {
const { user, pass } = yield take(LOGIN_REQUEST) //等待 Store 上指定的 action LOGIN_REQUEST
try {
let { data } = yield call(request.post, '/login', { user, pass }); // 阻塞,请求后台数据
yield fork(loadUserData, data.uid); // 非阻塞执行loadUserData
yield put({ type: LOGIN_SUCCESS, data }); // 发起一个action,类似于dispatch
} catch(error) {
yield put({ type: LOGIN_ERROR, error });
}
}
}
export function* loadUserData(uid) {
try {
yield put({ type: USERDATA_REQUEST });
let { data } = yield call(request.get, `/users/${uid}`);
yield put({ type: USERDATA_SUCCESS, data });
} catch(error) {
yield put({ type: USERDATA_ERROR, error });
}
}
提出一个问题:
Firebase提供的登录接口为何使用起来简单许多,而以上这些方案却相对复杂了许多?
[React] 14 - Redux: Redux Saga的更多相关文章
- Flux --> Redux --> Redux React 入门
本文的目的很简单,介绍Redux相关概念用法 及其在React项目中的基本使用 假设你会一些ES6.会一些React.有看过Redux相关的文章,这篇入门小文应该能帮助你理一下相关的知识 一般来说,推 ...
- React,关于redux的一点小见解
最近项目做多页面应用使用到了,react + webpack + redux + antd去构建多页面的应用,本地开发用express去模拟服务端程序(个人觉得可以换成dva).所以在这里吐槽一下我自 ...
- 在React中使用Redux
这是Webpack+React系列配置过程记录的第六篇.其他内容请参考: 第一篇:使用webpack.babel.react.antdesign配置单页面应用开发环境 第二篇:使用react-rout ...
- [React] 11 - Redux: redux
Ref: Redux中文文档 Ref: React 讀書會 - B團 - Level 19 Redux 深入淺出 Ref: React+Redux 分享會 Ruan Yifeng, Redux 架构: ...
- Flux --> Redux --> Redux React 基础实例教程
本文的目的很简单,介绍Redux相关概念用法 及其在React项目中的基本使用 假设你会一些ES6.会一些React.有看过Redux相关的文章,这篇入门小文应该能帮助你理一下相关的知识 一般来说,推 ...
- react系列(五)在React中使用Redux
上一篇展示了Redux的基本使用,可以看到Redux非常简单易用,不限于React,也可以在Angular.Vue等框架中使用,只要需要Redux的设计思想的地方,就可以使用它. 这篇主要讲解在Rea ...
- Flux --> Redux --> Redux React 入门 基础实例使用
本文的目的很简单,介绍Redux相关概念用法 及其在React项目中的基本使用 假设你会一些ES6.会一些React.有看过Redux相关的文章,这篇入门小文应该能帮助你理一下相关的知识 一般来说,推 ...
- [RN] React Native 使用 Redux 比较详细和深刻的教程
React Native 使用 Redux 比较详细和深刻的教程 React Native 使用 Redux https://www.jianshu.com/p/06fc18cef56a http:/ ...
- React Hooks +React Context vs Redux
React Hooks +React Context vs Redux https://blog.logrocket.com/use-hooks-and-context-not-react-and-r ...
随机推荐
- 3dmax多个版本软件的安装包以及安装教程
这个文档具体出自哪里,我也是记不得了,需要的看下,链接如果是失效,那我也无能为力了. 免费分享,链接永久有效 2014版3D MAX链接:http://pan.baidu.com/s/1nuFr7Xv ...
- v$instance如何生成
参考:http://www.itpub.net/thread-1284858-1-1.html 1.ORACLE 先创建的x$ 表即RDBMS的内部表 2.然后在X$表的基础上创建了GV$ 视图. ...
- PHP 计算两个时间戳之间相差的时间
//功能:计算两个时间戳之间相差的日时分秒 //$begin_time 开始时间戳 //$end_time 结束时间戳 function timediff($begin_time,$end_time) ...
- Linux TC(Traffic Control)框架原理解析
近日的工作多多少少和Linux的流控有点关系.自打几年前知道有TC这么一个玩意儿而且多多少少理解了它的原理之后,我就没有再动过它,由于我不喜欢TC命令行,实在是太繁琐了.iptables命令行也比較繁 ...
- [Python] 文件扫描
文件扫描 下载 https://github.com/YouXianMing/FileManager 细节 1. 基于Python 3.60,其他版本未测试 2. 支持扫描深度,不设置则扫描全部,设置 ...
- Revit中如何给不同构件着色
在Revit构件密集,默认的显示模式难以区分不同构件的区别,比如建筑立面有很多不同的机电管道,风管.水管,电缆桥架等,可一个给不同的机电管线添加不同的颜色,以示其区别,如下图所示,完成着色后,各种不同 ...
- 谷歌Chrome浏览器无法安装插件的解决方法
Chrome浏览器已替代了个人多年使用的遨游浏览器,但众所周知,国内的环境无法正常登录谷歌账户.无法访问应用商店,而Chrome主版本号大于66的只能从Chrome应用商店下载并安装插件,这不是死结吗 ...
- MDX 查询原型
本篇文章记录 SBS 中 MDX 查询原型,可以根据这些查询原型来解决实际项目中的问题. 1. 查询在 2004年1月2日 - 2004年3月1日之间购买过 Bikes 产品的用户. SELECT ( ...
- 如何清空iframe中的内容?
我都是用这种方法往iframe里面添加内容的. document.frames["iframe1"].document.write("<img src='loadi ...
- WineBottler for Mac(Mac 运行 exe 程序工具)安装
1.软件简介 WineBottler 是 macOS 系统上一款模拟 Windows 环境的工具,让你能够在 Mac 上安装 Windows 软件,类似于知名的 Crossover,但 Wine ...