You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Redux执行removeItem后报Can't read properties of 'undefined'错误

报错根因

两处问题直接触发报错,附带一处语法隐患:

  • 核心错误在removeItem reducer的返回逻辑: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不传依赖数组会每次渲染都触发执行,很容易出现状态不同步的问题。
  • 附带语法错误:createList reducer定义末尾漏了闭合花括号,会直接导致编译失败。
修复步骤

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 08:33:12