React可切换Tabs切换时全量渲染问题修复(无需状态存储)
问题修复方案
为什么所有Tab都会重新渲染?
当activeTab状态变化时,Tabs组件会整体重新渲染,此时:
- 每个
Tab的clickprop是每次渲染都新建的箭头函数,导致props引用发生变化 isActive的值会对每个Tab重新计算,虽然大部分Tab的isActive值没变,但因为Tab组件没有做缓存优化,会跟着父组件一起重新渲染
修复步骤
1. 用React.memo缓存Tab组件
React.memo会对组件的props做浅比较,只有当props真正变化时才触发重新渲染:
import React, { memo } from 'react'; interface ITab { click: () => void; isActive: boolean; children: React.ReactNode; } // 用memo包裹Tab组件 const Tab = memo<ITab>(({ click, isActive, children }) => { console.log('tab', children); return ( <div onClick={click} className={classNames(styles.tab, { [styles.tabActive]: isActive })}> {children} </div> ); });
2. 用useCallback稳定click函数
避免每次渲染创建新的click函数,保证未切换的Tab的click prop引用不变:
import React, { useState, useCallback } from 'react'; const Tabs = () => { const tabs = ['ONE', 'TWO', 'THREE', 'FOUR', 'FIVE']; const [activeTab, setActiveTab] = useState(0); const getIsActive = (index: number) => activeTab === index; // 用useCallback稳定点击函数,setActiveTab本身是稳定的,所以依赖项为空 const handleTabClick = useCallback((index: number) => { setActiveTab(index); }, []); return ( <div className={styles.tabs}> {tabs.map((tab, index) => ( <Tab key={tab} isActive={getIsActive(index)} click={() => handleTabClick(index)} > {tab} </Tab> ))} </div> ); };
修改后,切换标签时只有之前激活的Tab和新激活的Tab会重新渲染,其他Tab因props无变化会被React.memo跳过渲染。
能否不使用状态存储解决?
严格来说不行——切换标签必须跟踪当前激活的状态,只是存储位置可以调整:
- 不用
useState的话,可以用useRef存储激活索引,但需要手动触发组件重新渲染(比如用一个空状态的useState模拟forceUpdate) - 也可以把状态存在URL hash中(如
#tab-1),通过监听hashchange事件更新激活状态,这种方式状态存在浏览器地址栏,但本质还是需要状态跟踪
完全不存储状态的话,无法记住当前激活的标签,也就实现不了切换功能。
内容的提问来源于stack exchange,提问作者Сергей Сивков
相关产品推荐
相关产品推荐

