前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Redux(二):组织reducer

Redux(二):组织reducer

原创
作者头像
Ashen
修改2020-06-01 14:29:47
4900
修改2020-06-01 14:29:47
举报
文章被收录于专栏:Ashenの前端技术

reducer是一个可预测的纯函数,接收2个参数:当前的state和action,然后返回更新后的state。

一、初始reducer

代码语言:javascript
复制
const initialState = {
  visibilityFilter:"SHOW_ALL",
  todos:[]
};

function appReducer(state=initialState,action){
  switch (action.type){
    case "SET_VISIBILITY_FILTER":{
      return Object.assign({},state,{
        visibilityFilter:action.filter
      });
    }
    case "ADD_TODO":{
      return Object.assign({},state,{
        todos:state.todos.concat({
          id:action.id,
          text:action.text,
          completed:false
        })
      })
    }
    case "TOGGLE_TODO":{
      return Object.assign({},state,{
        todos:state.todos.map(todo=>{
          if(todo.id !== action.id){
            return todo;
          }
          return Object.assign({},todo,{
            completed:!todo.completed
          })
        })
      })
    }
    case "EDIT_TODO":{
      return Object.assign({},state,{
        todos:state.todos.map(todo=>{
          if(todo.id !== action.id){
            return todo;
          }
          return Object.assign({},todo,{
            text : action.text
          })
        })
      })
    }
    default:{
      return state;
    }
  }
}

这是一个比较简单的reducer,包含2个状态区域:todos和visibilityFilter,4个子条件语句对应的action.type分别为:SET_VISIBILITY_FILTER、ADD_TODO、TOGGLE_TODO、EDIT_TODO。在实际项目中,分支语句对应的action.type会非常多,如果写到一个reducer里这个函数会变的非常的臃肿,所以接下来需要对其进行逐步的拆分。

二、提取工具函数

期间大量的使用的Object.assign()方法,这是一个ES6函数用来创建一个新对象,可以将其提取成一个函数:

代码语言:javascript
复制
function updateObject(oldObject,newValues){
  return Object.assign({},oldObject,newValues);
}

当我们更新todos中的某一个todo是总是需要去遍历替换,所以也可以提取成一个函数:

代码语言:javascript
复制
function updateItemInArray(array,itemId,callback){
  array.map(item=>{
    if(item.id !== itemId){
      return item;
    }
    return callback(item);
  })
}

那么主体的reducer就是这样子了:

代码语言:javascript
复制
function appReducer(state=initialState,action){
  switch (action.type){
    case "SET_VISIBILITY_FILTER":{
      return updateObject(state,{
        visibilityFilter:action.filter
      });
    }
    case "ADD_TODO":{
      return updateObject(state,{
        todos:state.todos.concat({
          id:action.id,
          text:action.text,
          completed:false
        })
      });
    }
    case "TOGGLE_TODO":{
      return updateObject(state,{
        todos:updateItemInArray(state.todos,action.id,todo=>{
          return updateObject(todo,{
            completed:!todo.completed
          })
        })
      });
    }
    case "EDIT_TODO":{
      return updateObject(state,{
        todos:updateItemInArray(state.todos,action.id,todo=>{
          return updateObject(todo,{
            text:todo.text
          })
        })
      });
    }
    default:{
      return state;
    }
  }
}

三、提取case reducer

接着,将每一个case语句对应的逻辑封装成函数:

代码语言:javascript
复制
function setVisibilityFilter(state,action){
  return updateObject(state,{
    visibilityFilter:action.filter
  });
}
function addTodo(state,action){
  return updateObject(state,{
    todos:state.todos.concat({
      id:action.id,
      text:action.text,
      completed:false
    })
  });
}
function toggleTodo(state,action){
  return updateObject(state,{
    todos:updateItemInArray(state.todos,action.id,todo=>{
      return updateObject(todo,{
        completed:!todo.completed
      })
    })
  });
}
function editTodo(state,action){
  return updateObject(state,{
    todos:updateItemInArray(state.todos,action.id,todo=>{
      return updateObject(todo,{
        text:todo.text
      })
    })
  });
}
function appReducer(state=initialState,action){
  switch (action.type){
    case "SET_VISIBILITY_FILTER":return setVisibilityFilter(state,action);
    case "ADD_TODO":return addTodo(state,action);
    case "TOGGLE_TODO":return toggleTodo(state,action);
    case "EDIT_TODO":return editTodo(state,action);
    default:{return state;}
  }
}

四、提取case reducer

这个例子中包含2种数据状态:visibilityFilter和todos,且每一个case的代码块值关心其中的一个数据状态,所以简单改造一下:

代码语言:javascript
复制
function setVisibilityFilter(visibilityState,action){
  return action.filter;
}
function addTodo(todosState,action){
  return todosState.concat({
    id:action.id,
    text:action.text,
    completed:false
  });
}
function toggleTodo(todosState,action){
  return updateItemInArray(todosState,action.id,todo=>{
    return updateObject(todo,{
      completed:!todo.completed
    })
  });
}
function editTodo(todosState,action){
  return updateItemInArray(todosState,action.id,todo=>{
    return updateObject(todo,{
      text:todo.text
    })
  })
}

4个函数分成2类,所以接着编写2个子reducer:

代码语言:javascript
复制
function visibilityReducer(visibilityState="SHOW_ALL",action){
  switch (action.type){
    case "SET_VISIBILITY_FILTER":return setVisibilityFilter(visibilityState,action);
    default:return visibilityState;
  }
}
function todosReducer(todos=[],action){
  switch (action.type){
    case "ADD_TODO":return addTodo(todos,action);
    case "TOGGLE_TODO":return toggleTodo(todos,action);
    case "EDIT_TODO":return editTodo(todos,action);
    default:return todos;
  }
}

那么我们的根reducer需要做的仅仅是返回一个包含2个属性visibilityFilter和todos的对象,对象的属性值就是上边2个子reducer的执行结果:

代码语言:javascript
复制
function appReducer(state=initialState,action){
  return{
    visibilityFilter:visibilityReducer(state.visibilityState,action),
    todos:todosReducer(state.todos,action)
  }
}

五、减少样式模板代码

其实现在基本已经大功告成了,如果能将子reducer里的switch语句再精简一下会看起来更简约。所以我们需要先编写一个createReducer函数来生成一个函数,这个生成的函数就是我们的子reducer:

代码语言:javascript
复制
function createReducer(initialState,handlers){
  return function(state=initialState,action){
    if(handlers.hasOwnProperty(action.type)){
      return handlers[action.type](state,action);
    }else{
      return state;
    }
  }
}

那么之前的visibilityReducer、todosReducer就变成了这样子:

代码语言:javascript
复制
const visibilityReducer = createReducer("SHOW_ALL",{
  SET_VISIBILITY_FILTER:setVisibilityFilter
});
const todosReducer = createReducer([],{
  ADD_TODO:addTodo,
  TOGGLE_TODO:toggleTodo,
  EDIT_TODO:editTodo
});

其它地方都不用改动。

六、组合reducer

接着我们需要再优化一下根reducer,也就是appReducer。首先编写一个函数combineReducers来组合我们的子reducer:

代码语言:javascript
复制
function combineReducers(initialState,reducers){
  return function(state=initialState,action){
    return Object.entries(reducers).reduce((init,item)=>{
      const key = item[0];
      const callback = item[1];
      init[key] = callback(state[key],action);
      return init;
    },{});
  }
}

那么最终appReducer就是这样子了:

代码语言:javascript
复制
const appReducer = combineReducers(initialState,{
  visibilityFilter:visibilityReducer,
  todos:todosReducer
});

从redux中引入createStore创建store:

代码语言:javascript
复制
const store = createStore(combineReducers(initialState,{
  visibilityFilter:visibilityReducer,
  todos:todosReducer
}));

至此,大功告成~

Redux内置了combineReducers函数,与我们的功能本质上是相同的。Reducer本质上就是纯函数,每一次派发action都会导致Reducer的执行,而Reducer的内部通过条件语句下发到子reducer,最终计算出新的state状态树并更新store。接着依次执行通过subscribe注册的回调函数,那么这个回调函数就是关键了,如果都是同步函数,那放到一个数组中遍历依次执行即可,但如果是异步函数那就要用到接下来要讲的中间件了,可以说正是中间件系统极大的拓展了redux的功能,丰富了我们的应用。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

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

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、初始reducer
  • 二、提取工具函数
  • 三、提取case reducer
  • 四、提取case reducer
  • 五、减少样式模板代码
  • 六、组合reducer
相关产品与服务
消息队列 TDMQ
消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档