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

