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
相关产品推荐
相关产品推荐

