Redux 异步数据流方案对比

https://juejin.im/post/59e6cd68f265da43163c2821?utm_source=tuicool&utm_medium=referral

Redux 的核心理念是严格的单向数据流,只能通过 dispatch(action) 的方式修改 store,流程如下:

view ->  action -> reducer -> store

而在实际业务中往往有大量异步场景,最原始的做法是在 React 组件 componentDidMount 的时候初始化异步流,通过 callback 或者 promise 的方式在调用 dispatch(action),这样做把 view 层和 model 层混杂在一起,耦合严重,后期维护非常困难。
之前的文章 解读 Redux 中间件的原理 可以知道,中间件(middleware)改写了 dispatch方法,因此可以更灵活的控制 dispatch 的时机,这对于处理异步场景非常有效。因此 Redux 作者也建议用中间件来处理异步流。社区常见的中间件有 redux-thunkredux-promiseredux-sagaredux-observable 等。

redux-thunk:简单粗暴

作为 Redux 作者自己写的异步中间件,其原理非常简单:Redux 本身只会处理同步的简单对象 action,但可以通过 redux-thunk 拦截处理函数(function)类型的 action,通过回调来控制触发普通 action,从而达到异步的目的。其典型用法如下:

//constants 部分省略
//action creator
const createFetchDataAction = function(id) {
    return function(dispatch, getState) {
        dispatch({
            type: FETCH_DATA_START, 
            payload: id
        })
        api.fetchData(id) 
            .then(response => {
                dispatch({
                    type: FETCH_DATA_SUCCESS,
                    payload: response
                })
            })
            .catch(error => {
                dispatch({
                    type: FETCH_DATA_FAILED,
                    payload: error
                })
            }) 
    }
}
//reducer
const reducer = function(oldState, action) {
    switch(action.type) {
    case FETCH_DATA_START : 
        // 处理 loading 等
    case FETCH_DATA_SUCCESS : 
        // 更新 store 等处理
    case FETCH_DATA_FAILED : 
        // 提示异常
    }
}

可以看到采用 redux-thunk 后,action creator 返回的 action 可以是个 function,这个 function 内部自己会在合适的时机 dispatch 合适的普通 action。而这里面也没有什么魔法,redux-thunk 其核心源码如下:

const thunk = ({ dispatch, getState }) => next => action => {
    if (typeof action === 'function') {
      return action(dispatch, getState);
    }
    return next(action);
  };

如果 action 是个 function,便将 dispatch 方法传入该函数并执行之。
redux-thunk 在使用时非常方便,能满足大部分场景,缺点就是样板代码太多,写起来费劲了点。

redux-promise:将 promise 贯彻到底

redux-thunk 是将从 api 返回的 promise resolve 后 dispatch 成不同 action,那直接将这个 promise 作为 action 给 dispatch,让中间件来处理 resolve 这个过程,岂不是就可以少写些 .then().catch() 之类的代码了吗?redux-promise 正是解决了这个问题。同样是从后端去数据,其典型用法为:

const FETCH_DATA = 'FETCH_DATA'
//action creator
const getData = function(id) {
    return {
        type: FETCH_DATA,
        payload: api.fetchData(id) // 直接将 promise 作为 payload
    }
}
//reducer
const reducer = function(oldState, action) {
    switch(action.type) {
    case FETCH_DATA: 
        if (action.status === 'success') {
             // 更新 store 等处理
        } else {
                // 提示异常
        }
    }
}

这样下来比 redux-thunk 的写法瘦身不少。其核心源码与 redux-thunk 类似,如果 action或 action.payload 是 Promise 类型则将其 resolve,触发当前 action 的拷贝,并将 payload 设置为 promise 的 成功/失败结果。

export default function promiseMiddleware({ dispatch }) {
  return next => action => {
    if (!isFSA(action))  {// 判断是否是标准的 flux action
      return isPromise(action)
        ? action.then(dispatch)
        : next(action);
    }

    return isPromise(action.payload)
      ? action.payload.then(
          result => dispatch({ ...action, payload: result }),
          error => {
            dispatch({ ...action, payload: error, error: true });
            return Promise.reject(error);
          }
        )
      : next(action);
  };
}

仔细一看会发现 redux-promise 的写法里 reducer 收到 action 时就已经被 resolve 了,这样如果要处理 loading 这种情景就还得写额外代码,而且在 action 这样一个简单对象里增加 status 属性会给人不规范的感觉,这可能就是步子迈大了容易扯到蛋吧。

redux-thunk 和 redux-promise 用法实际上比较类似,都是触发一个 function/promise 让中间件自己决定 dispatch 真正异步数据的时机,这对于大部分场景来说已经足够了。但是对于异步情况更复杂的场景,我们往往要写很多业务代码,一个异步结果返回后可能需要对应修改 store 里多个部分,这样就面临一个困惑的问题:业务代码是放在 action 层还是 reducer 里?例如,管理员冻结某用户的账户,需要同时更新 store 里 AllUserList 和 PendingUserlist, 这时候面临两种选择 :

  1. 点击按钮时触发一个 PEND_USER 的 action,然后在 reducer 对应 switch 里同时更新 AllUserList 和 PendingUserlist
  2. 点击按钮时触发 REFRESH_USER_LIST 和 REFRESH_PENDING_USER_LIST 两个 action,然后在 reducer 里分别更新两处 store
    一般来说用户一个动作触发一个 action 更符合常理,但是可能其他地方又有复用 REFRESH_USER_LIST 的地方,将 action 拆的更新更利于复用,这时候就得做个取舍了。

redux-saga:精准而优雅

而 redux-saga 就可以很好的解决这个问题,它在原来 Redux 数据流中增加了 saga 层(不要在意这个诡异的名字

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值