React/Redux多页面应用购物车逻辑实现及报错求助
Redux购物车多页面适配问题解决方案
问题概述
之前在单页面应用(所有组件嵌套在App内)中用Redux实现的购物车逻辑正常,但切换到多页面路由应用后出现两个核心问题:
- Product页面无法将商品添加到购物车
- Cart页面触发报错:
Cannot convert undefined or null to object,错误定位在Object.keys(cart)代码处
错误详情
Cart页面报错信息:
Unexpected Application Error! Cannot convert undefined or null to object TypeError: Cannot convert undefined or null to object at Function.keys () at Cart
当前代码
index.jsx
import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App.jsx'; import VinylClocks from './Components/VinylClocks.jsx'; import CustomClocks from './Components/CustomClocks.jsx'; import About from './Components/About.jsx'; import Contact from './Components/Contact.jsx'; import Cart from './Components/Cart.jsx'; import ProductPage from './Components/ProductPage.jsx'; import './index.css'; import { createBrowserRouter, RouterProvider, Route, } from "react-router-dom"; import { Provider } from "react-redux"; import store from "./store/store.js"; const router = createBrowserRouter([ { path: "/", element: <App/>, }, { path: "vinylclocks", element: <VinylClocks/>, }, { path: "customclocks", element: <CustomClocks/>, }, { path: "about", element: <About/>, }, { path: "contact", element: <Contact/>, }, { path: "cart", element: <Cart/>, }, { path: "product/productpage/:name", element: <ProductPage/>, }, ]); ReactDOM.createRoot(document.getElementById('root')).render( <React.StrictMode> <RouterProvider router={router} /> <Provider store={store}> <App /> </Provider> </React.StrictMode>, )
ProductPage.jsx
import React from "react"; import "./ProductPage.css"; import Navbar from "./Navbar.jsx"; import Footer from "./Footer.jsx"; import { data } from "./data.js"; import { useParams } from 'react-router-dom'; import { addItem } from '../features/cart/cartSlice.js'; export default function ProductPage() { const { name } = useParams(); const item = data.find(item => item.name === name); const onClickHandler = (inventoryItem) => { dispatch(addItem(inventoryItem)); }; if (!item) { return <div>No item</div>; } return ( <> <Navbar/> <div className="productPage-container" key={item.id}> <div className="image-container"> <img src={item.img}/> </div> <div className="about-product"> <h2 className="product-name">{item.name}</h2> <span className="product-type">{item.type}</span> <span className="product-price">${item.price}</span> <p className="product-desc">{item.desc}</p> <button onClick={() => onClickHandler(inventoryItem)} className="addToCart"> Add to Cart</button> </div> </div> <Footer/> </> ); }
Cart.jsx
import React from 'react'; import {changeItemQuantity} from '../features/cart/cartSlice.js'; export default function Cart(props) { const { cart, dispatch } = props; const onInputChangeHandler = (name, input) => { if (input === '') { return; } const newQuantity = Number(input); dispatch(changeItemQuantity(name, newQuantity)); }; const cartElements = Object.keys(cart).map(createCartItem); const total = calculateTotal(cart); return ( <div id="cart-container"> <ul id="cart-items">{cartElements}</ul> <h3 className="total"> Total{' '} <span className="total-value"> {total} </span> </h3> </div> ); function createCartItem(name) { const item = cart[name]; if (item.quantity === 0) { return; } return ( <li key={name}> <p>{name}</p> <select className="item-quantity" value={item.quantity} onChange={(e) => { onInputChangeHandler(name, e.target.value); }} > {[...Array(100).keys()].map((_, index) => ( <option key={index} value={index}> {index} </option> ))} </select> </li> ); } };
修复步骤
1. 修正Redux Provider的包裹范围
当前代码中RouterProvider和Provider并列渲染,导致路由组件无法访问Redux Store。必须让Provider包裹RouterProvider,确保所有路由组件都能获取到Store。
修改index.jsx的渲染部分:
ReactDOM.createRoot(document.getElementById('root')).render( <React.StrictMode> <Provider store={store}> <RouterProvider router={router} /> </Provider> </React.StrictMode>, )
2. 修复ProductPage的添加购物车功能
- 缺少
useDispatch钩子,导致dispatch未定义 - 点击按钮时传递了未定义的
inventoryItem变量
修改后的ProductPage.jsx:
import React from "react"; import "./ProductPage.css"; import Navbar from "./Navbar.jsx"; import Footer from "./Footer.jsx"; import { data } from "./data.js"; import { useParams } from 'react-router-dom'; import { useDispatch } from 'react-redux'; import { addItem } from '../features/cart/cartSlice.js'; export default function ProductPage() { const { name } = useParams(); const dispatch = useDispatch(); const item = data.find(item => item.name === name); const onClickHandler = (inventoryItem) => { dispatch(addItem(inventoryItem)); }; if (!item) { return <div>No item</div>; } return ( <> <Navbar/> <div className="productPage-container" key={item.id}> <div className="image-container"> <img src={item.img}/> </div> <div className="about-product"> <h2 className="product-name">{item.name}</h2> <span className="product-type">{item.type}</span> <span className="product-price">${item.price}</span> <p className="product-desc">{item.desc}</p> <button onClick={() => onClickHandler(item)} className="addToCart"> Add to Cart</button> </div> </div> <Footer/> </> ); }
3. 修复Cart组件的报错问题
- 当前通过props接收
cart和dispatch,但路由渲染时未传递,导致cart为undefined - 改用
useSelector和useDispatch钩子直接从Redux Store获取数据 - 补充未定义的
calculateTotal函数 - 处理
cart为空的情况,避免Object.keys报错
修改后的Cart.jsx:
import React from 'react'; import { useSelector, useDispatch } from 'react-redux'; import {changeItemQuantity} from '../features/cart/cartSlice.js'; export default function Cart() { const cart = useSelector(state => state.cart || {}); const dispatch = useDispatch(); const onInputChangeHandler = (name, input) => { if (input === '') { return; } const newQuantity = Number(input); dispatch(changeItemQuantity(name, newQuantity)); }; function calculateTotal(cart) { return Object.values(cart).reduce((total, item) => { return total + (item.price * item.quantity); }, 0).toFixed(2); } const cartElements = Object.keys(cart).map(createCartItem); return ( <div id="cart-container"> <ul id="cart-items">{cartElements}</ul> <h3 className="total"> Total{' '} <span className="total-value"> ${calculateTotal(cart)} </span> </h3> </div> ); function createCartItem(name) { const item = cart[name]; if (!item || item.quantity === 0) { return null; } return ( <li key={name}> <p>{name}</p> <select className="item-quantity" value={item.quantity} onChange={(e) => { onInputChangeHandler(name, e.target.value); }} > {[...Array(100).keys()].map((_, index) => ( <option key={index} value={index}> {index} </option> ))} </select> </li> ); } };
额外说明
- 确保
cartSlice.js的初始状态为一个空对象(如initialState: {}),避免cart为null/undefined calculateTotal函数逻辑需根据实际的cart数据结构调整,确保商品对象包含price和quantity字段
内容的提问来源于stack exchange,提问作者Igor
相关产品推荐
相关产品推荐

