Material UI卡片视图渲染触发操作函数引发更新溢出问题排查
问题根源与修复方案
嘿,我一眼就揪出问题所在了!你误用了Material UI Fab组件的属性,这直接引发了无限更新循环。
核心错误:搞错了Fab的事件属性
Material UI的Fab组件并没有action这个点击事件处理属性——你把它和onClick搞混了!更关键的是,Fab的action实际上是一个Ref属性(用于获取组件实例的操作对象)。当你把箭头函数传给action时,React会将其识别为「函数式Ref」,在组件挂载/更新阶段自动调用这个函数。
这就导致你的deleteItem和addItem在组件每次渲染时都会被执行,每次执行又触发setState,组件重新渲染后再次调用函数,形成无限循环,最终抛出了Maximum update depth exceeded错误。
修复步骤
1. 替换action为正确的onClick属性
把所有Fab组件的action换成onClick,这才是Fab的点击事件处理函数:
// 删除项的Fab <Fab onClick={() => deleteItem(entry.key)}> <DeleteIcon /> </Fab> // 添加项的Fab <Fab onClick={() => addItem()}> <AddIcon /> </Fab>
2. 给列表项添加唯一key(最佳实践)
你在gridItemMap.map返回的外层Grid组件上没有设置key,这会触发React的渲染警告,也可能导致潜在的渲染混乱。给它加上和子项一致的唯一key:
{gridItemMap.map( (entry) => ( <Grid key={entry.key} container direction="row" spacing={1} > <GridItem {...entry.props}></GridItem> <Fab onClick={() => deleteItem(entry.key)}> <DeleteIcon /> </Fab> </Grid> ))}
3. 使用函数式更新修改状态(避免闭包问题)
当你的新状态依赖于当前状态时,应该使用setState的函数形式,这样能确保每次更新都基于最新的状态,避免闭包捕获旧状态导致的错误(比如连续点击添加按钮时,只会添加一个项的问题):
function deleteItem(key: string) : void { console.log("deleting " + key); setGridItemMap(prevMap => prevMap.filter(value => value.key !== key)); } function addItem() : void { // 生成唯一key,避免重复 const newKey = `key${Date.now()}`; setGridItemMap(prevMap => prevMap.concat({key : newKey, props: props1})); }
修改后的完整代码
const App: React.FC = () => { function deleteItem(key: string) : void { console.log("deleting " + key); setGridItemMap(prevMap => prevMap.filter(value => value.key !== key)); } function addItem() : void { const newKey = `key${Date.now()}`; setGridItemMap(prevMap => prevMap.concat({key : newKey, props: props1})); } const props1 : GridItemProps = {title:"TitleProp1", body:"BodyProp1"}; const [gridItemMap, setGridItemMap] = useState([ {key: "key1", props: props1}, ]); return ( <Container maxWidth="sm"> <Grid container direction="column" justify="space-evenly" alignItems="stretch" spacing={1} > {gridItemMap.map( (entry) => ( <Grid key={entry.key} container direction="row" spacing={1} > <GridItem {...entry.props}></GridItem> <Fab onClick={() => deleteItem(entry.key)}> <DeleteIcon /> </Fab> </Grid> ))} <Fab onClick={() => addItem()}> <AddIcon /> </Fab> </Grid> </Container> ); }
这些修改后,你的组件就能正常工作,不会再出现无限循环的问题了。
内容的提问来源于stack exchange,提问作者I_Adze
相关产品推荐
相关产品推荐

