React+Redux应用中ADD_TRACK reducer写法及状态组织咨询
问题背景
我正在用React和Redux开发我的第一个应用,状态需要存储包含作者与标题的列表,每个列表下要存储10条包含URL、描述等信息的条目。为了便于管理和扩展,我决定采用带ID的对象而非数组来存储状态。目前已经写了ADD_LIST的action和reducer,现在有两个疑问:
- 如何编写
ADD_TRACKreducer,让每条track关联对应的列表ID? - 当前的方案是否合理?是否应该通过
ADD_LISTaction来添加track?
现有Action代码
const addList = ( { id, listAuthor = '', listTitle = '', } = {} ) => { return { type: 'ADD_LIST', id: uuid(), list: { listAuthor, listTitle, } }; }; const addTrack = ( { id, url = '', trackInfo = '', description = '', } = {} ) => ({ type: 'ADD_TRACK', track: { id: uuid(), url, trackInfo, description } });
现有Reducer代码
export default (state = listReducerDefaultState, action) => { switch (action.type) { case 'ADD_LIST': return { listIdArray: [...state.listIdArray, action.id], listById: { ...state.listById, [action.id]: action.list, } }; // 这里需要添加ADD_TRACK的处理逻辑 } };
解决方案
1. 实现关联列表ID的ADD_TRACK Reducer
首先啊,你的addTrack action漏了个关键参数:要关联的列表ID(比如命名为listId)!不然我们根本不知道这条track要归属到哪个列表里,先把action调整一下:
const addTrack = ( { listId, url = '', trackInfo = '', description = '', } = {} ) => ({ type: 'ADD_TRACK', listId, track: { id: uuid(), url, trackInfo, description } });
接下来在reducer中处理ADD_TRACK逻辑。我们需要给每个列表对象初始化存储track的结构——和你管理列表的思路保持一致,用trackIdArray维护track的展示顺序,trackById实现快速查找:
export default (state = listReducerDefaultState, action) => { switch (action.type) { case 'ADD_LIST': return { listIdArray: [...state.listIdArray, action.id], listById: { ...state.listById, [action.id]: { ...action.list, // 为新列表初始化track的存储结构 trackIdArray: [], trackById: {} } } }; case 'ADD_TRACK': { const { listId, track } = action; // 先校验目标列表是否存在,避免出现无效操作报错 if (!state.listById[listId]) { return state; } return { ...state, listById: { ...state.listById, [listId]: { ...state.listById[listId], // 更新track的顺序数组,保证展示顺序正确 trackIdArray: [...state.listById[listId].trackIdArray, track.id], // 将新track存入列表的trackById对象中 trackById: { ...state.listById[listId].trackById, [track.id]: track } } } }; } default: return state; } };
这样每条track就会精准关联到对应的列表ID,同时保持了状态结构的一致性,后续的查询、更新、删除操作都会非常容易维护。
2. 方案合理性分析 & 关于ADD_LIST添加track的建议
方案合理性
你选择的带ID的对象+顺序数组的状态结构,是Redux官方推荐的规范化状态结构,非常合理:
- 对象结构允许通过ID快速定位列表或track,时间复杂度为O(1),比数组遍历查找的O(n)效率高很多;
- 顺序数组可以维护列表和track的操作顺序,解决了对象本身无序的问题;
- 这种结构扩展性极强,后续添加更新、删除列表或track的功能时,逻辑会非常清晰,不会出现数组操作时的索引混乱问题。
是否通过ADD_LIST添加track
绝对不建议这么做!ADD_LIST的职责应该是创建一个新的空列表,而添加track是独立的业务操作,应该由专门的ADD_TRACK action来处理。这么做的优势:
- 符合单一职责原则,每个action只处理一件事,代码可读性和可维护性更强;
- 适配更多使用场景:用户通常是先创建列表,之后逐步添加track,而不是创建列表时一次性添加所有条目;
- 后续扩展更灵活:如果需要实现“给已有列表新增track”的功能,直接复用
ADD_TRACK即可,无需修改ADD_LIST的逻辑。
如果确实有“创建列表时同时添加初始track”的需求,你可以在组件中先调用addList获取新列表ID,再循环调用addTrack添加初始条目;或者专门创建一个ADD_LIST_WITH_TRACKS action来处理这个特定场景,不要把两种逻辑混在ADD_LIST中,避免逻辑耦合。
内容的提问来源于stack exchange,提问作者slwjc

