React类组件setState更新购物车总数总价报错排查
React类组件购物车功能报错修复
报错根因
- 类型错误
Cannot read properties of undefined (reading 'length')触发原因:- 构造函数内重复给
this.state赋值,第一次定义的shoppingCart字段被第二次赋值完全覆盖,state中不存在cartItems属性,访问this.state.cartItems.length直接触发undefined读取错误 - 代码中存在多处非法属性访问:从localStorage取出的
cartItems是render内的局部变量,并未挂载到this.state下,this.cartItems属于完全不存在的实例属性 - 存在拼写错误:数组累加方法
reduce被错写为redeuce - 未做空值兜底:localStorage中不存在
cart字段时,JSON.parse返回值为null,直接读取属性会触发报错
- 构造函数内重复给
- 性能警告
[Violation] 'message' handler took 177ms触发原因:- 每次render执行时都同步读取localStorage做IO操作,重复执行无缓存的数组计算,长任务阻塞了浏览器主线程
修复后完整代码
import { Component } from 'react'; class Cart extends Component{ constructor(props){ super(props) // 统一初始化所有状态,禁止重复赋值state this.state = { cartItems: [], totalItems: 0, totalPrice: 0 } // 绑定事件方法this指向 this.handleDecrease = this.handleDecrease.bind(this); this.handleIncrease = this.handleIncrease.bind(this); } // 组件挂载完成后读取本地存储,不在render中做IO/计算操作 componentDidMount() { const storedCart = JSON.parse(localStorage.getItem('cart')) || []; this.updateCartState(storedCart); } // 抽离公共状态更新逻辑,所有购物车变更统一走该方法 updateCartState(items) { // 统一计算总数量、总价格,做兜底处理 const totalItems = items.length; const totalPrice = items.reduce((sum, item) => sum + item.price * item.quantity, 0); // 必须调用setState触发视图更新 this.setState({ cartItems: items, totalItems, totalPrice }); // 同步更新本地存储 localStorage.setItem('cart', JSON.stringify(items)); } // 减少商品数量逻辑 handleDecrease(productId) { const updatedItems = this.state.cartItems.map(item => { if (item.id === productId && item.quantity > 1) { return {...item, quantity: item.quantity - 1} } return item; }).filter(item => item.quantity > 0); // 数量为0时移除商品 this.updateCartState(updatedItems); } // 增加商品数量逻辑 handleIncrease(productId) { const updatedItems = this.state.cartItems.map(item => { if (item.id === productId) { return {...item, quantity: item.quantity + 1} } return item; }); this.updateCartState(updatedItems); } render(){ // 统一从state解构取值,不访问不存在的实例属性 const { cartItems, totalItems, totalPrice } = this.state; return(<> <div> <h2>YOUR CART</h2> <p># Of Items: <span>{totalItems}</span></p> <p>Total Price: <span>${totalPrice.toFixed(2)}</span></p> <p>Check Out</p> </div> <div className="container prod-cntr"> <div className="row prod-row"> {cartItems.map((element) => ( <div className="col-lg-3 prod-col" key={element.id}> <div className="card card-container"> <img src={element.image} alt="product img" className="prod-img" /> <div className="card-body"> <p className="card-title">{element.product}</p> <p className="card-text">{element.description}</p> <p className="quantity"> <span className="decrease" onClick={() => this.handleDecrease(element.id)}> <i className="fa-solid fa-minus"></i> </span> Quantity: {element.quantity} <span className="increase" onClick={() => this.handleIncrease(element.id)}> <i className="fa-solid fa-plus"></i> </span> </p> </div> </div> </div> ))} </div> </div> </>) } } export default Cart;
关键修复说明
- 移除构造函数中重复的
this.state赋值,所有状态字段统一初始化,从根源避免状态丢失 - 禁止在render方法中执行IO操作、复杂计算:将localStorage读取逻辑移到
componentDidMount生命周期,仅在组件挂载时执行一次,解决主线程阻塞的性能问题 - 抽离统一的状态更新方法,所有购物车数据变更必须通过
setState触发,符合React单向数据流要求,避免视图和状态不同步 - 修正所有非法属性访问、拼写错误,增加空值兜底逻辑,从根源解决undefined读取报错
- 补全加减数量按钮的交互逻辑,变更后自动重算总金额、总数量,同步更新本地存储
内容的提问来源于stack exchange,提问作者madQuestions
相关产品推荐
相关产品推荐

