使用Refs获取React Tabs元素尺寸实现激活态底部滑块跟随效果
Tabs 滑块位置计算问题修复
核心问题梳理
- 违反React Hook调用规则:不能在
map循环中调用useRef,Hook只能在组件顶层作用域调用 - Ref使用逻辑错误:
tabsRef是循环生成的ref数组,数组本身没有current属性,只有数组内每个单独的ref实例才有current指向DOM元素 - 变量名拼写错误:定义的状态更新函数是
setSliderInfo,实际调用写成了setSliderWidth,会直接抛出未定义错误 - 依赖项配置错误:
tabsRef每次组件渲染都会重新生成新数组,会导致useEffect无限触发,且需要监听的是激活态value的变化,切换Tab时才需要更新滑块位置 - 定位值计算逻辑缺失:滑块是相对于父容器
StyledTabsContainer绝对定位,直接取Tab元素相对于视口的left会导致定位错位,需要减去父容器的左偏移量得到相对位置
修复后代码
const Tabs = ({renderTab, options, value}) => { // option是存储每个Tab配置的数组,value是当前激活Tab的状态值 const [sliderInfo, setSliderInfo] = useState({width: 0, left: 0}); // 存储所有Tab的DOM引用,单个ref存储数组,不要在循环内调用useRef const tabsRef = useRef(new Array(options.length).fill(null)); // 父容器ref,用于计算相对偏移 const containerRef = useRef(null); const currentTab = options.findIndex((tab) => tab.value === value ); // 计算滑块位置的公共方法 const updateSliderPosition = () => { if (currentTab < 0 || !tabsRef.current[currentTab] || !containerRef.current) return; const tabRect = tabsRef.current[currentTab].getBoundingClientRect(); const containerRect = containerRef.current.getBoundingClientRect(); setSliderInfo({ width: tabRect.width, // 计算相对于父容器的左偏移 left: tabRect.left - containerRect.left }); }; // 激活Tab变化、组件挂载、options变化时更新滑块 useEffect(() => { updateSliderPosition(); // 窗口尺寸变化时也要更新滑块位置 window.addEventListener('resize', updateSliderPosition); return () => window.removeEventListener('resize', updateSliderPosition); }, [value, options]); return ( <StyledTabsContainer ref={containerRef}> {options.map((tab, i) => { return ( <div key={i} className='wrapper' ref={(el) => tabsRef.current[i] = el} > { renderTab(tab) } </div> ) })} <TabSlider width={sliderInfo.width} left={sliderInfo.left} /> </StyledTabsContainer> ) }
补充说明
如果你的TabSlider需要实现流畅的切换过渡,给滑块添加transition: all 0.3s ease类CSS属性即可。
内容的提问来源于stack exchange,提问作者lisovskiy329
相关产品推荐
相关产品推荐

