You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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>
)
}

问题原因与修复方案

问题根源

  1. DOM操作时机错误:组件顶层直接调用document.getElementById和document.querySelectorAll时,DOM还未完成渲染,拿到的是null或空集合,导致事件监听无法绑定。
  2. 初始位置未设置:页面刷新后,没有主动触发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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 06:01:25