ReactJS使用MUI TabPanel时触发Maximum Update Depth Exceeded错误求助
问题排查与修复方案
核心原因分析
出现Maximum Update Depth Exceeded无限循环错误,主要来自两个关键点:
TabPanel组件定义位置错误
你将TabPanel定义在父组件的函数内部,这会导致每次父组件渲染(比如exampleState更新触发父组件重渲染)时,TabPanel都会被重新创建为全新的组件类型。此时MyTestComponent会被强制卸载再重新挂载,挂载时useEffect触发dispatch更新exampleState,再次引发父组件重渲染,形成无限循环。setSelectedExampleData未做引用稳定处理
该函数在MyTestComponent每次渲染时都会生成新的引用,作为props传给SomeOtherComponent后,可能触发子组件不必要的重渲染,进一步放大循环问题。
修复方案
方案1:将TabPanel移到父组件外部定义
把TabPanel组件的定义抽离到父组件函数外部,避免每次父组件渲染都生成新的组件类型,从而阻止子组件反复挂载卸载:
// 把TabPanel放在父组件外部定义 const TabPanel = (props: TabPanelProps): JSX.Element => { const { children, value, index, ...other } = props; return ( <div role="tabpanel" hidden={value !== index} id={`tabpanel-${index}`} aria-labelledby={`tab-${index}`} {...other} > {value === index && ( <Typography component="span"> {children} </Typography> )} </div> ); }; // 父组件函数 const ParentComponent = () => { const [selectedTabValue, setSelectedTabValue] = useState(0); const handleChangeTab = (event: React.ChangeEvent<{}>, newValue: number): void => { setSelectedTabValue(newValue); }; return ( <> <Tabs value={selectedTabValue} onChange={handleChangeTab} indicatorColor="primary" textColor="primary" variant="fullWidth" > {tabs.map((item) => ( <Tab label={item.label} key={item.label} disableRipple disableTouchRipple /> ))} </Tabs> <TabPanel value={selectedTabValue} index={0}> <MyTestComponent/> </TabPanel> <TabPanel value={selectedTabValue} index={1}> <SomeOtherTestComponent/> </TabPanel> </> ); };
方案2:用useCallback稳定setSelectedExampleData引用
通过useCallback包裹函数,确保其引用在依赖不变时保持稳定,减少子组件不必要的重渲染,同时让useEffect的依赖更可靠:
import { useCallback } from 'react'; const MyTestComponent = (): JSX.Element => { const classes = useStyles(); const { someRouteId } = useParams<Params>(); const { exampleDispatch, exampleState } = useContext(ExampleContext); const [testData, setTestData] = useState<GroupSummary[]>(exampleState.exampleData); const [index, setIndex] = useState(pageSize); // 用useCallback包裹函数,声明依赖项 const setSelectedExampleData = useCallback((id: number): void => { exampleDispatch({ type: ExampleActionTypes.SetSelectedExampleData, data: exampleState.exampleData.find(x => x.ID === id)!, }); }, [exampleDispatch, exampleState]); useEffect(() => { setSelectedExampleData(parseInt(someRouteId)); }, [someRouteId, setSelectedExampleData]); // 添加稳定后的函数作为依赖 return ( <> {testData?.length > 0 && ( testData.map(x=> ( <div key={`${x.ID}`}> <SomeOtherComponent testData={x} setSelectedExampleData={setSelectedExampleData} /> </div> )) )} <Button handleClick={seeMore} text="See More" /> </> ); };
额外优化:同步testData与exampleState
如果testData需要跟随exampleState.exampleData变化,可添加useEffect同步两者,避免显示不一致:
useEffect(() => { setTestData(exampleState.exampleData); }, [exampleState.exampleData]);
内容的提问来源于stack exchange,提问作者devdev
相关产品推荐
相关产品推荐

