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 (
            
{this.props.data.get('count')}
) } }
  • 声明两种映射关系

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)

你可能感兴趣的:(redux-saga的使用)