Redux处理ADD_ITEM action时触发TypeError:state[action.payload.savedVideos]未定义
问题核心诱因
报错的完整触发链路可以拆解为两层原因:
- 状态结构和代码取值逻辑完全不匹配:你当前的
initialState顶层只有saves这一个数组属性,根本不存在和action.payload.savedVideos取值对应的属性。不管你调用addItem时给第二个参数传了什么值(是字段名、还是视频id),用这个值去取state下的属性时,拿到的永远是undefined。 - 缺少兜底校验触发迭代错误:你在reducer里直接写了
[...state[action.payload.savedVideos], action.payload.item]的逻辑,当取到的值是undefined时,数组展开运算符会尝试调用这个值上的Symbol.iterator方法,自然就会抛出对应的TypeError报错。
另外你的参数命名也存在语义混淆的问题:把代表“笔记关联的YouTube视频标识”的参数命名为savedVideos,很容易在写取值逻辑时和真实的状态结构搞混。
可落地的解决方案
按照你的业务需求(为每个YouTube视频单独保存笔记),按照以下步骤调整代码即可解决问题:
先把状态结构调整到和业务逻辑匹配
原有initialState缺少专门存笔记的容器,需要补充对应结构,避免第一次访问就取到undefined:const initialState = { saves: [ // 原有视频条目保持不变 { id: 1, title: 'Hello meow', created: '18 may 2018', link: 'https://www.youtube.com/watch?v=-QmyosHh-kU', content: 'Nowy film gargamela!', }, // ... 其余原有视频条目省略 ], notes: {} // 新增notes对象,key为视频id,value为该视频下的笔记数组 };修正action creator的参数语义
把原来有歧义的savedVideos参数改成明确的videoId,从源头避免传值逻辑混乱:export const addItem = (itemContent, videoId) => { const getId = () => `_${Math.random().toString(36).substr(2, 9)}`; return { type: 'ADD_ITEM', payload: { videoId, // 传入当前笔记关联的视频id item: { id: getId(), ...itemContent, }, }, }; };修正reducer的处理逻辑,补上兜底校验
不要直接展开可能为undefined的属性,对取到的列表做空数组兜底,同时把笔记更新的逻辑放到新增的notes容器下,不要直接往state顶层塞动态属性:const reducer = (state = initialState, action) => { switch (action.type) { case 'ADD_ITEM': return { ...state, notes: { ...state.notes, // 取对应视频的笔记列表,不存在就默认是空数组 [action.payload.videoId]: [ ...(state.notes?.[action.payload.videoId] ?? []), action.payload.item ] } }; // ... 其余原有case分支保持不变 } };
注意:如果你的需求不是按视频分类存笔记,而是把所有笔记统一存在一个列表里,那完全不需要写动态key,直接把reducer里的动态取值替换成固定的
notes数组字段即可,不要用容易混淆的动态属性写法。
内容的提问来源于stack exchange,提问作者TeodorWolski
相关产品推荐
相关产品推荐

