redux-saga框架使用详解及Demo教程

前面我们讲解过redux框架和dva框架的基本使用,因为dva框架中effects模块设计到了redux-saga中的知识点,可能有的同学们会用dva框架,但是对redux-saga又不是很熟悉,今天我们就来简单的讲解下saga框架的主要API和如何配合redux框架使用

redux-saga 官方地址

http://leonshi.com/redux-saga-in-chinese/index.html

Demo运行效果图

  • todoList

gif
  • CounterApp

gif

示例Demo地址

redux-saga-Demo作者还是按照以前的风格,提供了两个不同的版本,简单的 CounterApp, 稍复杂的 TodoList

  • CounterApp

https://github.com/guangqiang-liu/redux-saga-counterApp

  • todoList

https://github.com/guangqiang-liu/redux-saga-todoListDemo

什么是redux-saga

redux-saga 是一个用于管理 Redux 应用异步操作的中间件(又称异步 action)。 redux-saga 通过创建 Sagas 将所有的异步操作逻辑收集在一个地方集中处理,可以用来代替 redux-thunk 中间件。

这意味着应用的逻辑会存在两个地方:

  • Reducers 负责处理 action 的 state 更新

  • Sagas 负责协调那些复杂或异步的操作

Sagas是通过Generator函数来创建的,如果有不熟悉 Generator函数使用的,请查看阮老师对Generator的介绍

Sagas 不同于thunks,thunks 是在action被创建时调用,而 Sagas只会在应用启动时调用(但初始启动的 Sagas 可能会动态调用其他 Sagas),Sagas 可以被看作是在后台运行的进程,Sagas 监听发起的action,然后决定基于这个 action来做什么:是发起一个异步调用(比如一个 fetch 请求),还是发起其他的action到Store,甚至是调用其他的 Sagas

在 redux-saga 的世界里,所有的任务都通用 yield Effects 来完成(Effect 可以看作是 redux-saga 的任务单元)。Effects 都是简单的 Javascript 对象,包含了要被 Saga middleware 执行的信息(打个比方,你可以看到 Redux action其实是一个个包含执行信息的对象), redux-saga 为各项任务提供了各种Effect创建器,比如调用一个异步函数,发起一个action到Store,启动一个后台任务或者等待一个满足某些条件的未来的 action

redux-saga框架核心API

一、Saga 辅助函数

redux-saga提供了一些辅助函数,用来在一些特定的action 被发起到Store时派生任务,下面我先来讲解两个辅助函数:takeEvery 和 takeLatest

  • takeEvery

例如:每次点击 Fetch 按钮时,我们发起一个 FETCH_REQUESTED 的 action。 我们想通过启动一个任务从服务器获取一些数据,来处理这个action

首先我们创建一个将执行异步 action 的任务:

import { call, put } from 'redux-saga/effects'

export function* fetchData(action) {
try {
const data = yield call(Api.fetchUser, action.payload.url);
yield put({type: "FETCH_SUCCEEDED", data});
} catch (error) {
yield put({type: "FETCH_FAILED", error});
}
}

然后在每次 FETCH_REQUESTED action 被发起时启动上面的任务

import { takeEvery } from 'redux-saga'

function* watchFetchData() {

  yield* takeEvery("FETCH_REQUESTED", fetchData)
}

注意:上面的 takeEvery 函数可以使用下面的写法替换

function* watchFetchData() {

   while(true){
yield take('FETCH_REQUESTED');
yield fork(fetchData);
}
}
  • takeLatest

在上面的例子中,takeEvery 允许多个 fetchData 实例同时启动,在某个特定时刻,我们可以启动一个新的 fetchData 任务, 尽管之前还有一个或多个 fetchData 尚未结束

如果我们只想得到最新那个请求的响应(例如,始终显示最新版本的数据),我们可以使用 takeLatest 辅助函数

import { takeLatest } from 'redux-saga'

function* watchFetchData() {
yield* takeLatest('FETCH_REQUESTED', fetchData)
}

和takeEvery不同,在任何时刻 takeLatest 只允许执行一个 fetchData 任务,并且这个任务是最后被启动的那个,如果之前已经有一个任务在执行,那之前的这个任务会自动被取消

二、Effect Creators

redux-saga框架提供了很多创建effect的函数,下面我们就来简单的介绍下开发中最常用的几种

  • take(pattern)
  • put(action)
  • call(fn, ...args)
  • fork(fn, ...args)
  • select(selector, ...args)

take(pattern)

take函数可以理解为监听未来的action,它创建了一个命令对象,告诉middleware等待一个特定的action, Generator会暂停,直到一个与pattern匹配的action被发起,才会继续执行下面的语句,也就是说,take是一个阻塞的 effect

用法:

function* watchFetchData() {
while(true) {
// 监听一个type为 'FETCH_REQUESTED' 的action的执行,直到等到这个Action被触发,才会接着执行下面的 yield fork(fetchData) 语句
yield take('FETCH_REQUESTED');
yield fork(fetchData);
}
}

put(action)

put函数是用来发送action的 effect,你可以简单的把它理解成为redux框架中的dispatch函数,当put一个action后,reducer中就会计算新的state并返回,注意: put 也是阻塞 effect

用法:

export function* toggleItemFlow() {
let list = []
// 发送一个type为 'UPDATE_DATA' 的Action,用来更新数据,参数为 `data:list`
yield put({
type: actionTypes.UPDATE_DATA,
data: list
})
}

call(fn, ...args)

call函数你可以把它简单的理解为就是可以调用其他函数的函数,它命令 middleware 来调用fn 函数, args为函数的参数,注意: fn 函数可以是一个 Generator 函数,也可以是一个返回 Promise 的普通函数,call 函数也是阻塞 effect

用法:

export const delay = ms => new Promise(resolve => setTimeout(resolve, ms))

export function* removeItem() {
try {
// 这里call 函数就调用了 delay 函数,delay 函数为一个返回promise 的函数
return yield call(delay, 500)
} catch (err) {
yield put({type: actionTypes.ERROR})
}
}

fork(fn, ...args)

fork 函数和 call 函数很像,都是用来调用其他函数的,但是fork函数是非阻塞函数,也就是说,程序执行完 yield fork(fn, args) 这一行代码后,会立即接着执行下一行代码语句,而不会等待fn函数返回结果后,在执行下面的语句

用法:

import { fork } from 'redux-saga/effects'

export default function* rootSaga() {
// 下面的四个 Generator 函数会一次执行,不会阻塞执行
yield fork(addItemFlow)
yield fork(removeItemFlow)
yield fork(toggleItemFlow)
yield fork(modifyItem)
}

select(selector, ...args)

select 函数是用来指示 middleware调用提供的选择器获取Store上的state数据,你也可以简单的把它理解为redux框架中获取store上的 state数据一样的功能 :store.getState()

用法:

export function* toggleItemFlow() {
// 通过 select effect 来获取 全局 state上的 `getTodoList` 中的 list
let tempList = yield select(state => state.getTodoList.list)
}

三、createSagaMiddleware()

createSagaMiddleware 函数是用来创建一个 Redux 中间件,将 Sagas 与 Redux Store 链接起来

sagas 中的每个函数都必须返回一个 Generator 对象,middleware 会迭代这个 Generator 并执行所有 yield 后的 Effect(Effect 可以看作是 redux-saga 的任务单元)

用法:

import {createStore, applyMiddleware} from 'redux'
import createSagaMiddleware from 'redux-saga'
import reducers from './reducers'
import rootSaga from './rootSaga' // 创建一个saga中间件
const sagaMiddleware = createSagaMiddleware() // 创建store
const store = createStore(
reducers,
将sagaMiddleware 中间件传入到 applyMiddleware 函数中
applyMiddleware(sagaMiddleware)
) // 动态执行saga,注意:run函数只能在store创建好之后调用
sagaMiddleware.run(rootSaga) export default store

四、middleware.run(sagas, ...args)

动态执行sagas,用于applyMiddleware阶段之后执行sagas

  • sagas: Function: 一个 Generator 函数
  • args: Array: 提供给 saga 的参数 (除了 Store 的 getState 方法)

注意:动态执行saga语句 middleware.run(sagas) 必须要在store创建好之后才能执行,在 store 之前执行,程序会报错

以CounterApp Demo来看redux-saga具体使用方式

**index.js **

import React from 'react';
import ReactDOM from 'react-dom';
import {createStore, applyMiddleware} from 'redux'
import createSagaMiddleware from 'redux-saga' import rootSaga from './sagas'
import Counter from './Counter'
import rootReducer from './reducers' const sagaMiddleware = createSagaMiddleware()
let middlewares = []
middlewares.push(sagaMiddleware) const createStoreWithMiddleware = applyMiddleware(...middlewares)(createStore)
const store = createStoreWithMiddleware(rootReducer) sagaMiddleware.run(rootSaga) const action = type => store.dispatch({ type }) function render() {
ReactDOM.render(
<Counter
value={store.getState()}
onIncrement={() => action('INCREMENT')}
onDecrement={() => action('DECREMENT')}
onIncrementAsync={() => action('INCREMENT_ASYNC')} />,
document.getElementById('root')
)
} render() store.subscribe(render)

sagas.js

import { put, call, take,fork } from 'redux-saga/effects';
import { takeEvery, takeLatest } from 'redux-saga' export const delay = ms => new Promise(resolve => setTimeout(resolve, ms)); function* incrementAsync() {
// 延迟 1s 在执行 + 1操作
yield call(delay, 1000);
yield put({ type: 'INCREMENT' });
} export default function* rootSaga() {
// while(true){
// yield take('INCREMENT_ASYNC');
// yield fork(incrementAsync);
// } // 下面的写法与上面的写法上等效
yield* takeEvery("INCREMENT_ASYNC", incrementAsync)
}

reducer.js

export default function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
case 'INCREMENT_ASYNC':
return state
default:
return state
}
}

从上面的代码结构可以看出,redux-saga的使用方式还是比较简单的,相比较之前的redux框架的CounterApp,多了一个sagas的文件,reducers文件还是之前的使用方式

总结

本文所讲解的基本上是redux-saga框架在开发中最常使用到的常用API,还有很多不常用API,请参照redux-saga官方文档:http://leonshi.com/redux-saga-in-chinese/docs/api/index.html

如果同学们看到文章这里,还是对redux-saga框架的基本使用不熟悉,概念模糊,建议看看作者提供的Demo示例

作者建议:同学们可以将作者之前讲解的redux框架和redux-saga框架对比来学习理解,这样更清楚他们之间的区别和联系。

更多文章

  • 作者React Native开源项目OneM【500+ star】地址(按照企业开发标准搭建框架完成开发的):https://github.com/guangqiang-liu/OneM:欢迎小伙伴们 star
  • 作者简书主页:包含60多篇RN开发相关的技术文章http://www.jianshu.com/u/023338566ca5欢迎小伙伴们:多多关注,多多点赞
  • 作者React Native QQ技术交流群:620792950 欢迎小伙伴进群交流学习
  • 友情提示:在开发中有遇到RN相关的技术问题,欢迎小伙伴加入交流群(620792950),在群里提问、互相交流学习。交流群也定期更新最新的RN学习资料给大家,谢谢大家支持!

小伙伴们扫下方二维码加入RN技术交流QQ群

QQ群二维码,500+ RN工程师在等你加入哦

redux-saga框架使用详解及Demo教程的更多相关文章

  1. dva框架使用详解及Demo教程

    dva框架的使用详解及Demo教程 在前段时间,我们也学习讲解过Redux框架的基本使用,但是有很多同学在交流群里给我的反馈信息说,redux框架理解上有难度,看了之后还是一脸懵逼不知道如何下手,很多 ...

  2. 【python3+request】python3+requests接口自动化测试框架实例详解教程

    转自:https://my.oschina.net/u/3041656/blog/820023 [python3+request]python3+requests接口自动化测试框架实例详解教程 前段时 ...

  3. python+requests接口自动化测试框架实例详解

    python+requests接口自动化测试框架实例详解   转自https://my.oschina.net/u/3041656/blog/820023 摘要: python + requests实 ...

  4. Hadoop 新 MapReduce 框架 Yarn 详解

    Hadoop 新 MapReduce 框架 Yarn 详解: http://www.ibm.com/developerworks/cn/opensource/os-cn-hadoop-yarn/ Ap ...

  5. 测试框架mochajs详解

    测试框架mochajs详解 章节目录 关于单元测试的想法 mocha单元测试框架简介 安装mocha 一个简单的例子 mocha支持的断言模块 同步代码测试 异步代码测试 promise代码测试 不建 ...

  6. 转: javascript模块加载框架seajs详解

    javascript模块加载框架seajs详解 SeaJS是一个遵循commonJS规范的javascript模块加载框架,可以实现javascript的模块化开发和模块化加载(模块可按需加载或全部加 ...

  7. Android热门网络框架Volley详解[申明:来源于网络]

    Android热门网络框架Volley详解[申明:来源于网络] 地址:http://www.cnblogs.com/caobotao/p/5071658.html

  8. laravel5.5的任务调度(定时任务)详解(demo)

    https://blog.csdn.net/LJFPHP/article/details/80417552 laravel5.5的定时任务详解(demo) 这篇文章写得挺详细的.看了它我基本就会用了 ...

  9. openlayers3教材详解及demo(完整)

            openlayers3教材详解及demo(完整)        OpenLayers 3对OpenLayers网络地图库进行了根本的重新设计.版本2虽然被广泛使用,但从JavaScri ...

随机推荐

  1. Hadoop生态体系

    Hadoop路线 HDFS  分布式文件系统 MapReduce  分布式编程框架 ZooKeeper  分布式协调框架 Hive  非关系型数据仓库 Flume  日志采集框架

  2. hadoop 3.0.0 alpha3 安装、配置

    1. 官网下载 wget  http://mirror.bit.edu.cn/apache/hadoop/common /hadoop-3.0.0-alpha3/hadoop-3.0.0-alpha3 ...

  3. html+css中常见的浏览器兼容性处理

    1.居中问题 div里的内容,IE默认为居中,而FF默认为左对齐,可以尝试增加代码margin: 0 auto; 2.高度问题 两上下排列或嵌套的div,上面的div设置高度(height),如果di ...

  4. 核心API

    1.ProcessEngine ProcessEngine是Activiti中最核心的类,其他的类都是由他而来.Activiti流程引擎的配置文件是名为 activiti.cfg.xml 的XML文件 ...

  5. <Android 应用 之路> 百度地图API使用(2)

    简介 上一篇只是大致的提一下百度地图API的Android SDK的基本内容,然后抄袭一个官网上的Demo,今天看一下百度地图的第一部分,地图类型和基本的显示. 简单实战 不同类型地图的显示 //设置 ...

  6. Git连接GitLab远程仓库

    1.简介 远程仓库是指托管在网络上的项目仓库,现在互联网上有很多项目托管平台,比如github.gitlab等.为了不公开自己项目代码,可以在自己的服务器上搭建自己的项目仓库,最常见的是搭建GitLa ...

  7. js下拉框去掉重复的

    想用jquery代码实现这样的一个功能:有一个下拉框,当选择下拉框的时候,判断选择的值有没有被选择过,如果有则提示:代码如下: $(function(){ var authTypes=new Arra ...

  8. ArrayList排序Sort()方法(转)

    //使用Sort方法,可以对集合中的元素进行排序.Sort有三种重载方法,声明代码如下所//示. public void Sort(); //使用集合元素的比较方式进行排序 public void S ...

  9. SVN 代码托管

    1.安装服务 使用yum安装subversion,简单.不繁琐. 1 yum install -y subversion 2.创建版本库 1 2       mkidr /svn/obj        ...

  10. 网络协议HTTP TCP/UDP 浏览器缓存 Restful(十)

    一 TCP网络协议 1 建立TCP连接:三次握手原则 客户端通过向服务器端发送一个SYN来创建一个主动打开,作为三次握手的一部分.客户端把这段连接的序号设定为随机数 A. 服务器端应当为一个合法的SY ...