You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ReactJS使用MUI TabPanel时触发Maximum Update Depth Exceeded错误求助

问题排查与修复方案

核心原因分析

出现Maximum Update Depth Exceeded无限循环错误,主要来自两个关键点:

  1. TabPanel组件定义位置错误
    你将TabPanel定义在父组件的函数内部,这会导致每次父组件渲染(比如exampleState更新触发父组件重渲染)时,TabPanel都会被重新创建为全新的组件类型。此时MyTestComponent会被强制卸载再重新挂载,挂载时useEffect触发dispatch更新exampleState,再次引发父组件重渲染,形成无限循环。

  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 21:47:09