React报错Uncaught Error: Too many re-renders,子组件修改父组件样式失败
问题描述
尝试通过子组件按钮的onMouseEnter/onMouseLeave事件修改父组件样式时,触发React报错:
Uncaught Error: Too many re-renders. React limits the number of renders to prevent an infinite loop
即使使用箭头函数绑定事件,仍出现该问题,需实现子组件按钮控制父组件样式的需求。
现有代码示例
App.js
import React, { useState, useEffect } from "react"; import Categories from "./Categories"; import ShopPage from "./components/Products"; export default function App() { const [data, setData] = useState(Categories); useEffect(() => { setData(data); }, []); return ( <div className="wrapper"> <ShopPage products={data} filterResult={filterResult} /> </div> ); }
ShopPage 组件
const ShopPage = ({ products }) => { return ( <> <div> <Products products={products} /> </div> </> ); };
Products 父组件
const Products = ({products}) => { const [hovered, setHovered] = useState(false); const [style, setStyle] = useState(false); if (hovered) { setStyle({ // 内联样式 }); } else { setStyle({ // 内联样式 }); } return ( <> <Product setHovered={setHovered} style={style} products={products}/> </> ); }; export default Products;
Product 子组件
const Product = ({ setHovered, style, products }) => { return ( <div className={styles.items}> {products.map((value) => { return ( <> <div style={style}> <button onMouseEnter={() => setHovered(true)} onMouseLeave={() => setHovered(false)} > Add to Cart </button> </div> </> ); })} </div> ); }; export default Product;
解决方案
错误原因
报错核心是Products组件在渲染阶段直接调用setStyle修改状态:每次组件渲染时,都会执行if-else中的setStyle,触发状态更新后组件会再次渲染,循环往复导致无限渲染。
修复方案
方案1:用useEffect监听状态变化更新样式
将样式更新逻辑放到useEffect中,仅当hovered状态改变时才执行,避免重复触发更新:
const Products = ({products}) => { const [hovered, setHovered] = useState(false); const [style, setStyle] = useState({}); // 仅在hovered状态变化时更新样式 useEffect(() => { setStyle(hovered ? { // 鼠标悬停时的样式示例 backgroundColor: "#f5f5f5", border: "1px solid #ddd" } : { // 鼠标离开时的样式示例 backgroundColor: "transparent", border: "1px solid #fff" }); }, [hovered]); // 依赖项设置为hovered return ( <> <Product setHovered={setHovered} style={style} products={products}/> </> ); }; export default Products;
方案2:直接根据状态计算样式(无需额外state)
如果样式不需要持久化存储,可以直接在渲染时根据hovered动态生成样式,减少不必要的状态变量:
const Products = ({products}) => { const [hovered, setHovered] = useState(false); // 实时根据hovered状态计算样式 const currentStyle = hovered ? { backgroundColor: "#f5f5f5", border: "1px solid #ddd" } : { backgroundColor: "transparent", border: "1px solid #fff" }; return ( <> <Product setHovered={setHovered} style={currentStyle} products={products}/> </> ); }; export default Products;
额外优化
- 删除App组件中冗余的useEffect:初始化state已经设置为
Categories,无需在useEffect中重复赋值,直接删除以下代码:
useEffect(() => { setData(data); }, []);
- 给map循环元素添加唯一key:避免React列表渲染警告,使用产品的唯一标识(如id)作为key:
{products.map((value) => { return ( <div key={value.id}> {/* 替换为产品实际的唯一属性 */} <div style={style}> <button onMouseEnter={() => setHovered(true)} onMouseLeave={() => setHovered(false)} > Add to Cart </button> </div> </div> ); })}
内容的提问来源于stack exchange,提问作者user20984971
相关产品推荐
相关产品推荐

