前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用命名空间复用 Reducer 逻辑

使用命名空间复用 Reducer 逻辑

作者头像
IMWeb前端团队
发布2019-12-03 16:24:15
6220
发布2019-12-03 16:24:15
举报
文章被收录于专栏:IMWeb前端团队

本文作者:IMWeb jaychen 原文出处:IMWeb社区 未经同意,禁止转载

本文作者:IMWeb howenhuo 原文出处:IMWeb社区 未经同意,禁止转载

常见 Reducer 冗余

在使用 Redux 开发应用的过程中,我们经常会复制黏贴一些相似的 reducer

举个例子,有两个页面,它们都包含一个数据列表,它们都含有相同的状态

代码语言:javascript
复制
const page1State = {
  loading: false, // 列表加载态(菊花)
  retcode: 0, // 列表CGI返回码
  page: 0, // 列表当前页码
  count: 10, // 列表每页数量
  pageTotal: 0, // 订单列表总共分页数量
  list: [], // 列表
  sort: -1 // 倒叙排序
};
const page2State = {
  loading: false, // 列表加载态(菊花)
  retcode: 0, // 列表CGI返回码
  page: 0, // 列表当前页码
  count: 10, // 列表每页数量
  pageTotal: 0, // 订单列表总共分页数量
  list: [], // 列表
  title: "" // 标题
};

接着我们可能会编写这样的 reducer

代码语言:javascript
复制
function page1Reducer(state = page1State, { type, data }) {
  switch (type) {
    case PAGE1_LOADING_LIST: {
      return Object.assign({}, state, {
        loading: true
      });
    }
    case PAGE1_GET_LIST: {
      return Object.assign({}, state, {
        loading: false,
        page: data.page,
        pageTotal: data.pageTotal,
        list: data.list || []
      });
    }
    case PAGE1_ERR_LIST: {
      return Object.assign({}, state, {
        loading: false,
        retcode: data.retcode
      });
    }
    default:
      return state;
  }
}

function page2Reducer(state = page2State, { type, data }) {
  switch (type) {
    case PAGE2_LOADING_LIST: {
      return Object.assign({}, state, {
        loading: true
      });
    }
    case PAGE2_GET_LIST: {
      return Object.assign({}, state, {
        loading: false,
        page: data.page,
        pageTotal: data.pageTotal,
        list: data.list || []
      });
    }
    case PAGE2_ERR_LIST: {
      return Object.assign({}, state, {
        loading: false,
        retcode: data.retcode
      });
    }
    default:
      return state;
  }
}

区分不同的 action type 主要是避免 reduer 错误处理。

但这样看上去是否很冗余?那么是否可以只用一个 list reducer,就能处理这种重复的流程呢?

使用命名空间

首先我们将相同的状态抽取出来

代码语言:javascript
复制
const listState = {
  loading: false, // 列表加载态(菊花)
  retcode: 0, // 列表CGI返回码
  page: 0, // 列表当前页码
  count: 10, // 列表每页数量
  pageTotal: 0, // 订单列表总共分页数量
  list: [] // 列表
};

const page1State = {
  ...listState,
  //其他字段
  sort: -1 // 倒叙排序
};

const page2State = {
  ...listState,
  //其他字段
  title: "" // 标题
};

接着把前面两个 reducer 相同的部分提取出来

代码语言:javascript
复制
function listReducer(reducerNamespace) {
  return (state, { namespace, type, data }) => {
    // 初始调用或者命名空间不一致的都不做处理
    if (state === undefined || reducerNamespace !== namespace) {
      return state;
    }
    switch (type) {
      case LOADING_LIST: {
        return Object.assign({}, state, {
          loading: true
        });
      }
      case GET_LIST: {
        return Object.assign({}, state, {
          loading: false,
          page: data.page,
          pageTotal: data.pageTotal,
          list: data.list || []
        });
      }
      case ERR_LIST: {
        return Object.assign({}, state, {
          loading: false,
          retcode: data.retcode
        });
      }
      default:
        return state;
    }
  };
}

这样原来的 Reducer 就简化成如下

代码语言:javascript
复制
const page1List = listReducer('PAGE1')
function page1Reducer(state = page1State, action) {
  state = page1List(state, action);
  switch (action.type) {
    // 其他逻辑
    default:
      return state;
  }
}

const page2List = listReducer('PAGE2')
function page2Reducer(state = page2State, action) {
  state = page2List (state, action);
  switch (action.type) {
    // 其他逻辑
    default:
      return state;
  }
}

最后我们在 dispatch 时,增加 namespace 字段就能指定处理哪一个列表的数据了

代码语言:javascript
复制
store.dispatch({
  namespace: "PAGE1",
  type:GET_LIST,
  data: {
    page: 1,
    pageTotal: 10,
    list: [1, 2, 3, 4, 5]
  }
});

store.dispatch({
  namespace: "PAGE2",
  type: ERR_LIST,
  data: {
    retcode: 404
  }
});

使用高阶函数抽象 reducer

虽然上面在 pageReducer 中已经抽象出相同的 listReducer,但写法上不是很优雅,我们再实现一个 composeReducers

代码语言:javascript
复制
function composeReducers(...reducers) {
  return (state, action) => {
    if (reducers.length === 0) {
      return state;
    }
    const last = reducers[reducers.length - 1]; 
    const rest = reducers.slice(0, -1);
    return rest.reduceRight(
      (enhanced, reducer) => reducer(enhanced, action),
      last(state, action)
    );
  };
}

利用这个 composeReducers 可以把原来 pageReducer 中耦合的 listReducer 分离出来,并且可以轻松的组合多个 Reducer

代码语言:javascript
复制
export default combineReducers({
  page1Reducer: composeReducers(page1Reducer, listReducer("PAGE1")),
  page2Reducer: composeReducers(page2Reducer, listReducer("PAGE2"))
});

例子源码

codesandbox

参考

Reducer 逻辑复用

重用 Redux 中的 reducer

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-05-15 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 常见 Reducer 冗余
  • 使用命名空间
  • 使用高阶函数抽象 reducer
  • 例子源码
  • 参考
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档