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

React Material如何阻止标签页切换时卸载内容?

问题:Material UI 3标签页切换后数据丢失、内容卸载的解决办法

我刚接触React不久,正在用Material Design 3 + React(TypeScript)做UI原型。用基础示例时碰到个问题:未选中的标签页会卸载内容。

我有个基础组件,通过props接收数据,把同一数据用不同渲染方式展示在3个标签页里。组件初始化传数据时,第一个标签页能正常加载显示内容,但切到第二或第三个标签页再切回第一个,所有标签页都变空白,数据丢了。

默认行为好像是每次切换标签页都会重新获取数据,这不是我想要的。有没有办法把数据存在变量里,只在标签页可见时重新加载?

我的基础代码:

interface Bob {
  firstName: string;
  lastName: string;
}  

interface BobProps {
  bob: Bob;
  children?: React.ReactNode;
  index: number;
  value: number;
} 

function TabPanel(props: BobProps) {
  const { children, value, index, ...other} = props;
  return (
    <div>
       role="tabpanel"
       hidden={value !== index}
       id={`bob-tab-panel-${index}`}
       {...other}>

     // Just to show there was data (appears under tab menus - but before tab content)
     <div>firstName: {props.Bob.firstName}, lastName: {props.bob.lastName}</div>
     <value == index && <Box sx={{ p:3 }}>{children}</Box>}

    </div>
  );
}

function allyProps(index: number) {
  return {
      id: `bob-tab-panel-${index}`
  };
}

function TestTab(bobsProps: Bob) {
  const [value, setValue] = React.useState(0); // default tab
  const handleChange = (event: any, newValue: any) => {
     setValue(newValue); // handle tab change
  }
return (
  <div>
     // Just to show there was data (appears above tab menus)
     <div>firstName: {bobsProps.firstName}, lastName: {bobsProps.lastName}</div>

     <Tabs value={value} onChange={handleChange}>
         <Tab label="one"   {...allyProps(0)} />
         <Tab label="two"   {...allyProps(1)} />
         <Tab label="three" {...allyProps(2)} />
     </Tabs>
     <TabPanel bob={bobsProps} value={value} index={0}>
        // want to display data (from state?)
     </TabPanel>
     <TabPanel bob={bobsProps} value={value} index={1}>
        // want to display data 
     </TabPanel>
     <TabPanel bob={bobsProps} value={value} index={2}>
        // want to display data
     </TabPanel>

  </div>
);
}

解决办法

1. 核心:避免TabPanel内容被卸载

默认的hidden属性会让React卸载不可见的TabPanel组件,导致数据丢失。改用CSS控制显示隐藏,让所有TabPanel始终保持挂载状态:

function TabPanel(props: BobProps) {
  const { children, value, index, bob, ...other } = props;
  return (
    <div
      role="tabpanel"
      style={{ display: value === index ? 'block' : 'none' }} // 用CSS控制显示/隐藏,不卸载组件
      id={`bob-tab-panel-${index}`}
      {...other}
    >
      <div>firstName: {bob.firstName}, lastName: {bob.lastName}</div>
      <Box sx={{ p: 3 }}>{children}</Box> {/* 直接渲染,不再用条件判断卸载 */}
    </div>
  );
}

2. 按需刷新数据(仅在标签页可见时加载)

如果需要在标签页激活时重新获取数据,可以用useEffect监听当前选中的标签索引:

function TestTab(bobsProps: Bob) {
  const [value, setValue] = React.useState(0);
  // 用对象存储每个标签页的数据,默认初始化传入的内容
  const [tabData, setTabData] = React.useState<Record<number, Bob>>({
    0: bobsProps,
    1: bobsProps,
    2: bobsProps
  });

  const handleChange = (event: any, newValue: number) => {
    setValue(newValue);
  };

  // 切换到目标标签页时,触发数据刷新
  React.useEffect(() => {
    // 这里替换成你的实际数据获取逻辑,比如调用API
    const refreshData = async () => {
      // const fetchedData = await fetchBobDetail(value);
      // setTabData(prev => ({ ...prev, [value]: fetchedData }));
    };
    refreshData();
  }, [value]);

  return (
    <div>
      <div>firstName: {bobsProps.firstName}, lastName: {bobsProps.lastName}</div>
      <Tabs value={value} onChange={handleChange}>
        <Tab label="one" {...allyProps(0)} />
        <Tab label="two" {...allyProps(1)} />
        <Tab label="three" {...allyProps(2)} />
      </Tabs>
      <TabPanel bob={tabData[0]} value={value} index={0}>
        <div>渲染方式1:{tabData[0].firstName} {tabData[0].lastName}</div>
      </TabPanel>
      <TabPanel bob={tabData[1]} value={value} index={1}>
        <div>渲染方式2:{tabData[1].firstName} - {tabData[1].lastName}</div>
      </TabPanel>
      <TabPanel bob={tabData[2]} value={value} index={2}>
        <div>渲染方式3:{tabData[2].lastName}, {tabData[2].firstName}</div>
      </TabPanel>
    </div>
  );
}

3. 修正原代码的语法错误

原代码有几个会导致报错的问题:

  • props.Bob.firstName 应为props.bob.firstName(大小写错误)
  • role="tabpanel" 写在了<div>标签外面,需移到标签属性内
  • 条件渲染value == index && <Box... 需用大括号包裹:{value === index && <Box...}

内容的提问来源于stack exchange,提问作者AndyW

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 11:13:17