React切换Tab标签页组件重复渲染两次,如何让组件仅渲染一次?
重复渲染根因
- 你的
openTabreducer 逻辑每次更新时都会全量重建所有openedTabs项的对象引用,哪怕某个标签的选中状态、其他属性没有任何变化,也会生成新的对象,导致所有<TabPanel>的props判定为变化,触发全量重渲染 - 渲染时在map循环内每次都执行
props.openedTabs.findIndex((t) => t.selected)计算选中索引,额外增加运行开销 - 使用数组索引作为
key,且未对自定义组件做渲染缓存 - 所有未选中的标签页默认也会执行渲染,哪怕是隐藏状态
优化方案
1. 优化reducer状态更新逻辑,保留未修改项的引用
只修改选中状态变化的标签的引用,其余标签保持原有对象引用不变,避免不必要的更新触发:
const openTab = (state, action) => { let newOpenedTabs = [...state.openedTabs]; // 先把之前选中的标签的选中状态取消 const prevSelectedIndex = newOpenedTabs.findIndex(t => t.selected); if (prevSelectedIndex !== -1) { // 只修改需要更新的项,其余项保留原引用 newOpenedTabs[prevSelectedIndex] = { ...newOpenedTabs[prevSelectedIndex], selected: false } } if (action.tab.singleTab) { const currentIndex = newOpenedTabs.findIndex(t => t.title === action.tab.title); if (currentIndex !== -1) { newOpenedTabs[currentIndex] = { ...newOpenedTabs[currentIndex], selected: true } } else { newOpenedTabs.push({ ...action.tab, selected: true }) } } else { newOpenedTabs.push({ ...action.tab, selected: true }) } return updateObject(state, { openedTabs: newOpenedTabs, }); };
2. 优化渲染层逻辑
- 提前计算选中索引,不要在循环内重复计算
- 用标签唯一标识(比如title)作为key,不要用数组索引
- 不需要预加载后台标签的话,直接只渲染当前选中的标签,避免渲染多余内容
- 用
React.memo包裹标签页内的自定义组件,props不变时跳过重渲染
优化后渲染代码:
// 提前计算一次选中索引 const selectedIndex = props.openedTabs.findIndex(t => t.selected); // 只渲染当前选中的标签(无需预加载场景) const activeTab = props.openedTabs[selectedIndex]; return activeTab ? ( <TabPanel value={selectedIndex} index={selectedIndex} className={classes.tabPanelItem} > {/* 不需要额外封装getComponent,直接渲染即可 */} <activeTab.component {...activeTab.props} /> </TabPanel> ) : null; // 如果需要保留后台标签的挂载状态,再用循环渲染,同时给TabPanel加memo缓存 /* props.openedTabs.map((tab, i) => ( <TabPanel value={selectedIndex} index={i} key={tab.title} // 用唯一标识做key className={classes.tabPanelItem} > <tab.component {...tab.props} /> </TabPanel> )) */
内容的提问来源于stack exchange,提问作者zahra zamani
相关产品推荐
相关产品推荐

