redux-saga的使用

一切优秀的框架都是为程序员服务的:reactjs提供更优雅的前端代码书写方式和更优的界面更新机制,react-native提供一种性能更优的混合开发方案,redux提供了组件和业务分离的解决方案,thunk或saga基于redux提供异步业务实现方案...
这篇文章只讲redux-saga的使用。先看一张图:


react-redux

图中的Middleware工作于redux内部,介于action和reducer之间,而saga只是某一种Middleware。要想使用它,首先得知道redux具体是如何运用的:

Redux的使用步骤

  • 首先定义所需组件

组件的功能很简单:只有一个文本和一个按钮,点击按钮将文本中的数字加1
另外,组件的props需要有count、data两个属性,前者是个函数(按钮的点击事件中会执行它),后者是个对象(文本的内容取自它),这是它向上层(这里可以指它的父组件或者redux)传达的需求

class SagaTestComp extends React.Component {

    _onCount = () => {
        //将当前count传给action watcher saga监听到这个action后 会将action传递给worker saga
        const curCount = this.props.data.get('count')
        this.props.count(curCount)
    }

    render() {
        console.log('SagaTestComp rendered')
        return (
            <div>
                <span>{this.props.data.get('count')}</span>
                <button onClick={this._onCount}>count</button>
            </div>
        )
    }
}
  • 声明两种映射关系

redux提供了映射组件的props中的对象属性和函数属性的方法

//映射组件props的数据部分
const mapStateToProps = (state) => {
    return {
        //data将作为所绑定的Component的props的一部分, 它的值取自redux的state对象的SagaTest属性
        data: state.SagaTest
    }
}
//映射组件props的函数部分
const mapDispatchToProps = (dispatch) => {
    return {
        //函数count将作为所绑定Component的props的一部分,函数体中执行了redux的dispatch方法
        count: (param) => {
            return dispatch(getCountAction(param))
        }
    }
}
  • 连接组件

本质就是向redux注册组件,这样redux才知道组件的props需要哪些内容

const Content = connect(
    mapStateToProps,
    mapDispatchToProps
)(SagaTestComp)
  • 定义会触发组件更新的事件

这些事件叫action,由组件触发;每种action由type属性唯一标识,不可重复

//计数
const getCountAction = (param) => {
    return {
        type: 'count',
        param
    }
}

//计数完成
const getCountFinishAction = (param) => {
    return {
        type: 'countFinish',
        param
    }
}
  • 定义修改redux中state的纯函数

这些纯函数叫reducer,每个reducer可接收两个参数:state、action;前者是这个reducer负责管辖的数据(改变它会引起redux中相应state的变化,从而引起组件更新),后者表示前面定义的诸多action中的某一个。每个reducer可通过action的type属性对指定的事件做出响应。

//初始数据 这里使用Immutable数据
const initData = {
    SagaTest: Immutable.fromJS({
        count: 0
    })
}

//reducer 纯函数 触发component的更新
const reducer = (state = initData, action) => {
    switch (action.type) {
        case 'countFinish':
            console.log(`reducer receive the action ${action.type}`)
            //因为redux的state是不可变数据 所以这里必须返回一个新对象才会触发component的更新
            return {
                SagaTest: state.SagaTest.update('count', () => action.param)//这里将计数更新成counter计算的结果
            }
        default:
            return state
    }
}

Redux-Saga的使用

前面讲了,saga是Middleware的一种,工作于action和reducer之间。如果按照原始的redux工作流程,当组件中产生一个action后会直接触发reducer修改state;而往往实际中,组件中发生的action后,在进入reducer之前需要完成一个异步任务,显然原生的redux是不支持这种操作的。
不过,实现异步操作的整体步骤是很清晰的:action被触发后,首先执行异步任务,待完成后再将这个action交给reducer。说到这里,thunk和saga的达到的效果是一致的,差别在于对action的处理完全不一样。
thunk采用的是扩展action的方式:使得redux的store能dispatch的内容从普通对象扩展到函数

mapDispatchToProps = (dispatch) => {
    return {
        delayBanner: () => dispatch(delayBanner)
    }
}

const delayBannerAction = {
    type: DELAY_BANNER
}

//至于什么时候真正执行dispatch方法 取决于业务本身
export const delayBanner = (dispatch) => {
    delayBannerTask(dispatch, delayBannerAction)
}

saga采用的方案更接近于redux的全局思想,使用方式和thunk有很大不同:
saga需要一个全局监听器(watcher saga),用于监听组件发出的action,将监听到的action转发给对应的接收器(worker saga),再由接收器执行具体任务,任务执行完后,再发出另一个action交由reducer修改state,所以这里必须注意:watcher saga监听的action和对应worker saga中发出的action不能是同一个,否则造成死循环
在saga中,全局监听器和接收器都使用Generator函数和saga自身的一些辅助函数实现对整个流程的管控
整个流程可以简单描述为
Component —> Action1 —> Watcher Saga —> Worker Saga —> Action2 —> Reducer —> Component
相比thunk,saga是多了一个action的,因为saga是将业务的触发(watcher saga)和业务的执行(worker saga)分开描述的

const getCountAction = (param) => {
    return {
        type: 'count',
        param
    }
}

const getCountFinishAction = (param) => {
    return {
        type: 'countFinish',
        param
    }
}

const mapDispatchToProps = (dispatch) => {
    return {
        count: (param) => {
            return dispatch(getCountAction(param))//这里dispatch的参数是个普通对象
        }
    }
}

//一个普通函数 将当前计数+1
function counter(curCount) {
    return ++curCount
}

//worker saga 这里其实是有接收参数的 这个参数action就是redux执行dispatch方法中的参数
function* counterSaga(action) {
    console.log(`counterSaga receive the action ${action.type}`)
    //接受到这个action之后 执行saga的call方法 call方法会执行counter函数 并将当前计数作为它的参数
    const countResult = yield call(counter, action.param)
    //counter执行的结果作为另一个action的参数
    yield put(getCountFinishAction(countResult))
}

//watcher saga 监听takeEvery这个action 并执行helloSaga
function* watchIncrementAsync() {
    console.log('watcher saga is listening to action')
    yield* takeEvery('count', counterSaga);
}

//执行watcher saga 这样就可以一直监听应用中的action了
sagaMiddleware.run(watchIncrementAsync)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容