Redux执行removeItem后报Can't read properties of 'undefined'错误
报错根因
两处问题直接触发报错,附带一处语法隐患:
- 核心错误在
removeItemreducer的返回逻辑:Redux Toolkit的createSlice基于Immer实现,只要reducer函数中显式写了return,返回值会直接替换整个slice的state。当前代码执行removeItem时,会找到目标清单后直接返回该清单过滤后的items数组,执行完action后,原本存储所有购物清单的state.shoppingLists会被替换成单个清单的商品数组,后续调用shoppingLists.find()匹配清单id时只能拿到undefined,读取listName属性时就会抛出"Can't read properties of 'undefined'"错误。 - 组件层冗余的本地状态+无依赖的useEffect加剧了问题:把Redux里能直接取到的清单数据额外存在本地state,且useEffect不传依赖数组会每次渲染都触发执行,很容易出现状态不同步的问题。
- 附带语法错误:
createListreducer定义末尾漏了闭合花括号,会直接导致编译失败。
修复步骤
1. 修正所有reducer逻辑
不要在操作单条清单数据时返回值替换整个state,用Immer支持的可变写法直接修改目标清单属性即可,同时补上缺失的语法闭合、增加空值判断兜底:
const shoppingListsSlice = createSlice({ name: "shoppingLists", initialState: [], reducers: { createList(state, action) { state.push({ listId: uuidv4(), listName: action.payload, items: [], color: "", createdAt: Date(), }); }, // 补上原代码缺失的闭合花括号 addItem(state, action) { const targetList = state.find((list) => list.listId === action.payload.listId); if (targetList) { targetList.items.push({ itemId: uuidv4(), itemName: action.payload.itemName, isBought: false, }); } }, removeItem(state, action) { const targetList = state.find((list) => list.listId === action.payload.listId); if (targetList) { // 直接更新目标清单的items数组,不返回值替换整个state targetList.items = targetList.items.filter( (item) => item.itemId !== action.payload.itemId ); } }, } });
2. 简化组件层逻辑
删除冗余的本地state和无意义的useEffect同步逻辑,Redux中可直接派生的数据不需要额外存本地副本,同时增加兜底判断避免清单不存在时的报错:
const { id } = useParams(); const dispatch = useDispatch(); const shoppingLists = useSelector((state) => state.shoppingLists); // 直接从Redux state派生当前选中的清单,无需额外本地存储 const selectedList = shoppingLists.find((list) => list.listId === id); const removeItemForm = (item) => { const { itemName, itemId } = item; dispatch(removeItem({ itemName, itemId, listId: id })); }; // 清单不存在/加载中时的兜底渲染 if (!selectedList) { return <div>当前清单不存在</div>; } return ( <div className="listHeading mb-4"> <h2>{selectedList.listName}</h2> {/* 其余清单渲染逻辑 */} </div> );
提示:如果业务场景确实需要把Redux数据同步到本地state,
useEffect必须传入正确的依赖数组,比如这里要传[findList],否则会出现无限重渲染、状态更新滞后的问题。但对于这类可以直接从全局state派生的数据,不建议额外存本地副本,多一层状态同步就多一层故障点。
内容的提问来源于stack exchange,提问作者seba sosa
相关产品推荐
相关产品推荐

