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

React页面复用同一表格组件导致数据丢失问题求助

问题:React标签页切换时电子表格数据丢失

在React应用中使用Material-UI的Tabs组件,两个标签页复用同一个Spreadsheet组件。在第一个标签页的表格输入数据后切换到第二个标签页,再切回第一个时,表格数据会被useState的默认值覆盖。

原标签页组件代码

import * as React from 'react';
import PropTypes from 'prop-types';
import Tabs from '@mui/material/Tabs';
import Tab from '@mui/material/Tab';
import Typography from '@mui/material/Typography';
import Box from '@mui/material/Box';

import Spreadsheet from './components/Spreadsheet.js'

function TabPanel(props) {
  const { children, value, index, ...other } = props;

  return (
    <div
      role="tabpanel"
      hidden={value !== index}
      id={`simple-tabpanel-${index}`}
      aria-labelledby={`simple-tab-${index}`}
      {...other}
    >
      {value === index && (
        <Box sx={{ p: 3 }}>
          <Typography>{children}</Typography>
        </Box>
      )}
    </div>
  );
}

TabPanel.propTypes = {
  children: PropTypes.node,
  index: PropTypes.number.isRequired,
  value: PropTypes.number.isRequired,
};

function a11yProps(index) {
  return {
    id: `simple-tab-${index}`,
    'aria-controls': `simple-tabpanel-${index}`,
  };
}

export default function BasicTabs() {
  const [value, setValue] = React.useState(0);

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

  return (
    <Box sx={{ width: '100%' }}>
      <Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
        <Tabs value={value} onChange={handleChange} aria-label="basic tabs example">
          <Tab label="Item One" {...a11yProps(0)} />
          <Tab label="Item Two" {...a11yProps(1)} />
          <Tab label="Item Three" {...a11yProps(2)} />
        </Tabs>
      </Box>
      <TabPanel value={value} index={0}>
        Item One
        <div id='div_spreadsheet1'>
          <Spreadsheet id="spreadsheet1"> </Spreadsheet>
        </div>
      </TabPanel>
      <TabPanel value={value} index={1}>
        Item Two
        <div id='div_spreadsheet12'>
          <Spreadsheet id="spreadsheet2"> </Spreadsheet>
        </div>
      </TabPanel>
      <TabPanel value={value} index={2}>
        Item Three
      </TabPanel>
    </Box>
  );
}

原Spreadsheet组件代码

import Spreadsheet from "react-spreadsheet";
import { useState } from "react";
  
export default function Sheet(){
  const [data, setData] = useState([
    [{ value: "" }, { value: "1" }],
  ]);
  return(
  <div>
    <h4>SpreadSheet</h4>
    <Spreadsheet data={data} onChange={setData} />
  </div> 
  )
    
};

问题原因

当前Sheet组件内部维护自身的data状态,但Material-UI的TabPanel通过value === index && (...)的条件渲染,会卸载未激活标签页内的组件。当切回第一个标签页时,Sheet组件重新挂载,useState会用初始值重新初始化,导致之前输入的数据丢失。


解决方案

方案1:状态提升到父组件(推荐)

将表格数据状态从子组件移到父组件BasicTabs中,为每个表格维护独立数据,通过props传递给子组件。这样即使子组件被卸载,数据仍保存在父组件中,切换标签时能恢复正确数据。

修改后的父组件代码

import * as React from 'react';
import PropTypes from 'prop-types';
import Tabs from '@mui/material/Tabs';
import Tab from '@mui/material/Tab';
import Typography from '@mui/material/Typography';
import Box from '@mui/material/Box';

import Sheet from './components/Spreadsheet.js'

function TabPanel(props) {
  const { children, value, index, ...other } = props;

  return (
    <div
      role="tabpanel"
      hidden={value !== index}
      id={`simple-tabpanel-${index}`}
      aria-labelledby={`simple-tab-${index}`}
      {...other}
    >
      {value === index && (
        <Box sx={{ p: 3 }}>
          <Typography>{children}</Typography>
        </Box>
      )}
    </div>
  );
}

TabPanel.propTypes = {
  children: PropTypes.node,
  index: PropTypes.number.isRequired,
  value: PropTypes.number.isRequired,
};

function a11yProps(index) {
  return {
    id: `simple-tab-${index}`,
    'aria-controls': `simple-tabpanel-${index}`,
  };
}

export default function BasicTabs() {
  const [value, setValue] = React.useState(0);
  // 为每个表格维护独立数据
  const [sheetData, setSheetData] = React.useState({
    spreadsheet1: [[{ value: "" }, { value: "1" }]],
    spreadsheet2: [[{ value: "" }, { value: "1" }]]
  });

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

  // 统一处理表格数据更新
  const handleSheetChange = (id, newData) => {
    setSheetData(prev => ({
      ...prev,
      [id]: newData
    }));
  };

  return (
    <Box sx={{ width: '100%' }}>
      <Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
        <Tabs value={value} onChange={handleChange} aria-label="basic tabs example">
          <Tab label="Item One" {...a11yProps(0)} />
          <Tab label="Item Two" {...a11yProps(1)} />
          <Tab label="Item Three" {...a11yProps(2)} />
        </Tabs>
      </Box>
      <TabPanel value={value} index={0}>
        Item One
        <div id='div_spreadsheet1'>
          <Sheet 
            id="spreadsheet1" 
            data={sheetData.spreadsheet1} 
            onChange={(newData) => handleSheetChange("spreadsheet1", newData)} 
          />
        </div>
      </TabPanel>
      <TabPanel value={value} index={1}>
        Item Two
        <div id='div_spreadsheet12'>
          <Sheet 
            id="spreadsheet2" 
            data={sheetData.spreadsheet2} 
            onChange={(newData) => handleSheetChange("spreadsheet2", newData)} 
          />
        </div>
      </TabPanel>
      <TabPanel value={value} index={2}>
        Item Three
      </TabPanel>
    </Box>
  );
}

修改后的Sheet组件代码

import Spreadsheet from "react-spreadsheet";
  
export default function Sheet({ data, onChange }){
  // 不再维护内部状态,直接使用父组件传入的props
  return(
  <div>
    <h4>SpreadSheet</h4>
    <Spreadsheet data={data} onChange={onChange} />
  </div> 
  )
    
};

方案2:保持组件挂载(避免卸载)

修改TabPanel的实现,用CSS隐藏未激活标签页,而不是卸载组件。这样组件会一直保持挂载状态,内部状态不会丢失。

修改后的TabPanel组件代码

function TabPanel(props) {
  const { children, value, index, ...other } = props;

  return (
    <div
      role="tabpanel"
      hidden={value !== index}
      id={`simple-tabpanel-${index}`}
      aria-labelledby={`simple-tab-${index}`}
      {...other}
      style={{
        display: value === index ? 'block' : 'none'
      }}
    >
      <Box sx={{ p: 3 }}>
        <Typography>{children}</Typography>
      </Box>
    </div>
  );
}

注意:此方法会让所有标签页组件保持挂载,若标签内容复杂可能增加内存占用,需根据场景权衡。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 19:10:35