React Router 6.4.3中NavLink激活链接时指示器无法移动问题
我用React的NavLink组件实现了导航栏(共5个链接),点击链接时指示器能正常跟随移动,但页面刷新后指示器功能就失效了。以下是相关代码和效果截图:
指示器的SCSS代码
#marker { position: absolute; top: 0; width: 41px; height: 41px; transition: 0.5s; z-index: 1; } #marker::before { content: ''; position: absolute; top: -10px; left: 20%; transform: translateX(-50%); width: 40px; height: 35px; border-radius: 8px; background: rgb(255, 255, 255); box-shadow: 0 0 10px rgb(58, 134, 255), 0 0 20px rgb(255, 255, 255), 0 0 40px #fff, 0 0 80px #fff, 0 0 160px rgb(58, 134, 255); }
效果对比
- 功能正常状态:

- 刷新后失效状态:

原NavLink代码片段
const Sidebar = () => { const marker = document.getElementById('marker') const items = document.querySelectorAll('ul li') function indicator(e) { marker.style.left = e.offsetLeft + 'px' marker.style.width = e.offsetWidth + 'px' } items.forEach((link) => { link.addEventListener('click', (e) => { indicator(e.target) }) }) function activeLink() { const current = document.querySelector('.active') indicator(current) } items.forEach((link) => { link.addEventListener('mouseover', (e) => { activeLink() }) }) const isActive = ({ isActive }) => (isActive ? 'active' : '') return ( <div className="navigation"> <ul> <li className="list"> <NavLink to="/about" className={isActive}> <FontAwesomeIcon className="icon" icon={faInfo} ></FontAwesomeIcon> </NavLink> </li> {/* 其余4个NavLink组件省略 */} <div id="marker"></div> </ul> </div> ) }
问题原因与修复方案
问题根源
- DOM操作时机错误:组件顶层直接调用
document.getElementById和document.querySelectorAll时,DOM还未完成渲染,拿到的是null或空集合,导致事件监听无法绑定。 - 初始位置未设置:页面刷新后,没有主动触发
activeLink去定位当前激活的NavLink,指示器停留在默认位置。
修复代码
import { useEffect, useRef } from 'react'; import { NavLink } from 'react-router-dom'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faInfo } from '@fortawesome/free-solid-svg-icons'; const Sidebar = () => { // 用useRef存储DOM元素,避免重复查询 const markerRef = useRef(null); const itemsRef = useRef([]); const indicator = (e) => { if (!markerRef.current) return; markerRef.current.style.left = `${e.offsetLeft}px`; markerRef.current.style.width = `${e.offsetWidth}px`; }; const activeLink = () => { const currentActive = document.querySelector('.active'); if (currentActive) indicator(currentActive); }; useEffect(() => { // 组件挂载后绑定事件监听 itemsRef.current.forEach(link => { link.addEventListener('click', (e) => indicator(e.target)); link.addEventListener('mouseover', activeLink); }); // 页面刷新时主动设置指示器初始位置 activeLink(); // 组件卸载时清理事件监听,避免内存泄漏 return () => { itemsRef.current.forEach(link => { link.removeEventListener('click', (e) => indicator(e.target)); link.removeEventListener('mouseover', activeLink); }); }; }, []); const isActive = ({ isActive }) => isActive ? 'active' : ''; return ( <div className="navigation"> <ul> <li className="list" ref={el => el && itemsRef.current.push(el)}> <NavLink to="/about" className={isActive}> <FontAwesomeIcon className="icon" icon={faInfo} /> </NavLink> </li> {/* 其余4个NavLink都需添加ref={el => el && itemsRef.current.push(el)} */} <div id="marker" ref={markerRef}></div> </ul> </div> ); }; export default Sidebar;
核心改进点
- 使用
useRef安全获取DOM元素,确保在DOM渲染完成后操作。 - 通过
useEffect在组件挂载阶段绑定事件,并主动调用activeLink设置初始位置。 - 添加清理函数移除事件监听,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Maksym
相关产品推荐
相关产品推荐

