React购物车勾选逻辑触发Too many renders无限重渲染报错
购物车勾选功能无限重渲染问题修复
问题根因
触发Too many renders...报错的核心原因如下:
- 事件绑定错误:复选框的
onChange属性直接写了handleCheck(index),属于渲染阶段立即执行函数调用,每次渲染都会触发state更新,state更新又触发重渲染,直接形成死循环 - 状态冗余错位:勾选状态属于全局购物车数据,在子组件
ShoppingItemList里额外存了一份本地checkedstate,多份状态同步极易出现异常;同时useCallback依赖项设置错误、直接修改浅拷贝后的原对象,都会触发不必要的重渲染 - 违反状态不可变原则:
[...props.shoppingList]是浅拷贝,直接修改updatedList[index].isSelected本质还是修改原状态对象的引用,React无法正确识别状态变更,容易出现渲染异常 - 逻辑判断无效:App组件中localStorage写入逻辑的判断条件
initShoppingItemList !== 0永远成立,会导致无意义的重复写入操作
修复步骤
1. 调整App.js状态逻辑
保持购物车数据为单一数据源,在父组件实现勾选状态切换方法,修复localStorage写入逻辑:
import { React, useState, useEffect, useCallback } from "react"; import ShoppingListForm from "./Components/ShoppingListForm"; import ShoppingItemList from "./Components/ShoppingItemList"; import { BrowserRouter as Router, Routes, Route } from "react-router-dom"; function App() { const initShoppingItemList = JSON.parse(localStorage.getItem("shoppedItem") || "[]"); const [shoppingItem, setShoppingItem] = useState(initShoppingItemList); const addShoppedItemHandler = (shoppedItem) => { const id = shoppingItem.length === 0 ? 0 : shoppingItem[shoppingItem.length - 1].id + 1; const shoppingListWId = { id: id, item: shoppedItem.item, quantity: shoppedItem.quantity, isSelected: shoppedItem.isSelected }; setShoppingItem([...shoppingItem, shoppingListWId]) }; // 新增勾选状态切换方法 const toggleItemSelected = useCallback((itemId) => { setShoppingItem(prevList => prevList.map(item => item.id === itemId ? {...item, isSelected: !item.isSelected} : item )) }, []) useEffect(() => { localStorage.setItem("shoppedItem", JSON.stringify(shoppingItem)); },[shoppingItem]); return ( <Router> <Routes> <Route path="/" element={<ShoppingListForm onSavedShoppingData={addShoppedItemHandler} />} /> <Route exact path="/itemList" element={<ShoppingItemList shoppingList={shoppingItem} onToggleSelect={toggleItemSelected} />} /> </Routes> </Router> ); } export default App;
2. 重写ShoppingItemList.js逻辑
删除子组件冗余的本地state,修正事件绑定方式,将复选框改为受控组件:
import { React } from "react"; import { Link } from "react-router-dom"; import "./ShoppingItemList.css"; const ShoppingItemList = (props) => { return ( <div className="container"> <div className="title">Shopping List:</div> <div className="list-container"> {props.shoppingList.map((item) => ( <ul key={item.id}> <input type="checkbox" checked={item.isSelected} onChange={() => props.onToggleSelect(item.id)} /> <div className="child">{item.item}</div> <div className="child">{item.quantity}</div> </ul> ))} </div> <Link to="/">Shopping Form</Link> <br></br> <div> 已勾选商品:{props.shoppingList.filter(i => i.isSelected).map(i => i.item).join('、')} </div> </div> ); }; export default ShoppingItemList;
关键修复点说明
- 事件绑定修正:
onChange传入箭头函数包裹调用逻辑,避免渲染阶段直接执行处理函数触发死循环 - 遵循单一数据源原则:所有购物车状态统一存在父组件App中,子组件只通过props接收数据和触发更新方法,消除多份状态同步问题
- 严格遵守状态不可变要求:更新选中状态时用
map返回全新对象,不直接修改原状态对象引用 - 复选框改为受控模式:给input绑定
checked属性和状态值对应,保证UI展示和业务状态完全同步 ShoppingListForm.js原有逻辑无问题,无需修改
内容的提问来源于stack exchange,提问作者divein
相关产品推荐
相关产品推荐

