React函数组件商品分类筛选出现更新深度超限错误求助
React电商项目分类筛选功能的无限更新循环问题
这是我基于React.js开发的首个电商项目,页面左侧展示商品分类,右侧展示商品列表。我尝试实现按分类筛选商品的功能,已接近完成但遇到了相关错误:
错误提示:
Warning: Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.
我哪里出现了错误?
核心逻辑代码
const [products, setProducts] = useState([]); const [page, setPage] = useState(1); const [categories, setCategories] = useState([]); const [selectedCategories, setSelectedCategories] = useState([]); //handling paging const handlePageChange = (pageNumber) => { if ( pageNumber > 0 && pageNumber <= products.length / 15 && pageNumber !== page ) setPage(pageNumber); } const addCategory = (category) => { if(!selectedCategories.includes(category)){ setSelectedCategories(prev => ([...prev, category])) } } const removeCategory = (category) => { if(selectedCategories.includes(category)){ const removedList = selectedCategories.filter((item) => (item !== category)); setSelectedCategories(removedList); } } const getCategories = () => { fetch('https://dummyjson.com/products/categories') .then(res => res.json()) .then((data) => { setCategories(data); }) .catch((err) => console.log(err)); } useEffect(() => { getCategories() }, []) const getProducts = () => { fetch('https://dummyjson.com/products?limit=90') .then(res => res.json()) .then((data) => { if (data.products && data.products.length) setProducts(data.products); }) .catch((err) => console.log(err)); } useEffect(() => { getProducts() }, []) useEffect(() => { if(selectedCategories.length === 0){ setProducts(products); } else{ setProducts(products.filter((item)=>(selectedCategories.includes(item.category)))); } }, [selectedCategories, products])
JSX代码
分类展示
{categories.map((category) => ( <div onClick={() => { if(selectedCategories.includes(category)){ removeCategory(category); } else{ addCategory(category); } }} key={category}> <div className="form-check"> <input className="form-check-input" type="checkbox" value={category} id={category} /> <label className="form-check-label" htmlFor={category}> {category} </label> </div> </div> ))}
商品列表展示
{products.length && ( <div className='row'> {products.slice(page * 15- 15, page * 15).map((item) => ( <div className='col-lg-4 mb-4 text-center' key={item.id}> <div className='product' key={item.id}> <Link to={`/shop/${item.id}/${item.title.replace(/\s+/g, '-').toLowerCase()}`}> <div className='list'> <div className='p_photo'> <img className='img-fluid' src={item.thumbnail} alt={item.title} /> </div> </div> </Link> <div className="Stars" style={{ "--rating": [item.rating] }} /> <div className='title mt-2'> <Link to={`/shop/${item.id}/${item.title.replace(/\s+/g, '-').toLowerCase()}`}>{item.title}</Link> </div> <div className='price mt-2'> <span className='sale'>${item.price}</span> <span className='discount'> ({item.discountPercentage}% OFF)</span> </div> </div> </div> ))} </div> )}
问题分析与修复方案
问题根源
- 无限更新循环:最后一个
useEffect的依赖数组包含products,但内部又调用setProducts修改该状态。每次products更新,useEffect就会重新执行,执行后又触发状态更新,形成无限循环。 - 丢失原始数据:直接修改
products状态进行筛选,会导致原始全量商品数据被覆盖,后续无法恢复到未筛选的状态。
修复步骤
1. 新增状态存储全量商品
把接口返回的完整商品列表单独存储,筛选时基于这个全量数据处理:
const [allProducts, setAllProducts] = useState([]); // 存原始全量商品 const [products, setProducts] = useState([]); // 存筛选后的商品 // 修改getProducts函数 const getProducts = () => { fetch('https://dummyjson.com/products?limit=90') .then(res => res.json()) .then((data) => { if (data.products && data.products.length) { setAllProducts(data.products); setProducts(data.products); // 初始化显示全量商品 } }) .catch((err) => console.log(err)); }
2. 修正筛选逻辑的useEffect
去掉products依赖,基于allProducts进行筛选,彻底避免循环:
useEffect(() => { if(selectedCategories.length === 0){ setProducts(allProducts); // 无选中分类时恢复全量商品 } else{ const filteredProducts = allProducts.filter(item => selectedCategories.includes(item.category)); setProducts(filteredProducts); } }, [selectedCategories, allProducts])
3. 优化分页逻辑(可选)
把分页计算抽成变量,让代码更清晰:
const maxPage = Math.ceil(products.length / 15); const handlePageChange = (pageNumber) => { if (pageNumber > 0 && pageNumber <= maxPage && pageNumber !== page) { setPage(pageNumber); } }
内容的提问来源于stack exchange,提问作者Firoz Khan
相关产品推荐
相关产品推荐

