[Redux] Reducer Composition with Arrays
In the previous lesson we created a reducer that can handle two actions, adding a new to-do, and toggling an existing to-do. Right now, the code to update the to-do item or to create a new one is placed right inside of the to-dos reducer.
This function is hard to understand because it makes us two different concerns, how the to-do's array is updated, and how individual to-dos are updated. This is not a problem unique to Redux. Any time a function does too many things, you want to extract other functions from it, and call them so that every function only addresses a single concern.
In this case, I decided that creating and updating a to-do in response to an action is a separate operation, and needs to be handled by a separate function called to-do. As a matter of convention, I decided that it should also accept two arguments, the current trait and the action being dispatched, and it should return the next trait.
But in this case, this trait refers to the individual to-do, and not to the least of to-dos. Finally, there is no magic in Redux to make it work. We extracted the to-do reducer from the to-dos reducer, so now we need to call it for every to-do, and assemble the results into an array.
While this is not required in this particular example, I suggest that you always have the default case where you return the current trait to avoid all [inaudible 1:36] in the future. The part described in this lesson is pervasive in Redux's development, and is called reducer composition.
Different reducers specify how different parts of the trait tree are updated in response to actions. Reducers are also normal JavaScript functions, so they can call other reducers to delegate and abstract a way of handling of updates of some parts of this tree they manage.
This pattern can be applied many times, and while there is still a single top level reducer managing the state of your app, you will find it convenient to express it as many reducers call on each other, each contribution to a part of the applications trait tree.
let todo = (state, action) => { switch(action.type){ case 'ADD_ITEM': return { text: action.text, id: action.id, completed: false }; case 'TOGGLE_ITEM': if(state.id !== action.id){ return state; }else{ return { ...state, completed: !state.completed // will overwirte the state object's completed prop }; } default: return state; } } let todos = (state = [], action) => { switch(action.type){ case 'ADD_ITEM': return state = [ ...state, todo(undefined, action) ]; case 'TOGGLE_ITEM': return state.map( (t) => todo(t, action)) default: return state; } }; let testTodo_addItem = () => { let stateBefore = []; let action = { type: 'ADD_ITEM', text: 'Learn Redux', id: 0 }; let stateAfter = [ { text: 'Learn Redux', id: 0, completed: false, } ]; deepFreeze(stateBefore); deepFreeze(action); expect( todos(stateBefore, action) ).toEqual(stateAfter); }; let testTodo_toggleItem = () => { let stateBefore = [ { text: 'Learn Redux', id: 0, completed: false }, { text: 'Learn Angular2', id: 1, completed: false } ]; let action = { type: 'TOGGLE_ITEM', id: 1 }; let stateAfter = [ { text: 'Learn Redux', id: 0, completed: false }, { text: 'Learn Angular2', id: 1, completed: true } ]; deepFreeze(stateBefore); deepFreeze(action); expect( todos(stateBefore, action) ).toEqual(stateAfter); } testTodo_toggleItem(); console.log("All tests passed!");
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具