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

React中onClick内操作DOM实现导航高亮:合规性与更优方案探讨

问题

我在React应用里做了个导航面板,用Link组件跳转不同页面,要高亮当前激活的导航链接。现在通过给激活链接加类实现了需求,代码如下:

import { Link } from "react-router-dom";

function Nav() {

    const highlight = (e) => {
        // Remove .nav-select class from all nav items
        let eles = document.querySelectorAll(".nav-select");
        eles.forEach.call(eles, (ele) => {
            ele.classList.remove('nav-select');
        })
        // Add .nav-select to currently clicked nav item
        e.target.className = e.target.className + " nav-select"
    }

    return (
        <nav className="main-nav">
            <ul>
                <li onClick={highlight}>
                    <Link to="/" >Home</Link>
                </li>
                <li onClick={highlight}>
                    <Link to="/blog" >Blog</Link>
                </li>
                <li onClick={highlight}>
                    <Link to="/clock" >Clock</Link>
                </li>
                <li onClick={highlight}>
                    <Link to="/video" >Video</Link>
                </li>
                <li onClick={highlight}>
                    <Link to="/goals" >Goals</Link>
                </li>
            </ul>
        </nav>
    );
};

export default Nav;

相关样式:

.main-nav .nav-select {
  border: 1px solid red;
  padding: 2px
}

这个实现能达到效果,但我知道直接调用document修改DOM属于副作用,最佳实践是用useEffect处理。想问这只是最佳实践,还是不遵循会出问题?有没有更好的实现或优化方案?

回答
  • 直接操作DOM不只是最佳实践问题,会引发实际bug:

    1. React的DOM diff算法可能会覆盖你手动修改的DOM状态,比如组件重新渲染时,手动添加的类可能被重置;
    2. 路由跳转不止点击链接这一种方式(比如浏览器前进/后退按钮、编程式导航),这时候你的onClick事件不会触发,高亮状态就会和实际路由不匹配。
  • 最优方案:用React Router内置的NavLink组件
    NavLink是Link的增强版,会自动给当前匹配的路由链接添加激活类(默认是active,也可以自定义),完全不需要手动操作DOM。

    优化后的代码:

    import { NavLink } from "react-router-dom";
    
    function Nav() {
        return (
            <nav className="main-nav">
                <ul>
                    <li>
                        <NavLink 
                            to="/" 
                            className={({ isActive }) => isActive ? "nav-select" : ""}
                        >Home</NavLink>
                    </li>
                    <li>
                        <NavLink 
                            to="/blog" 
                            className={({ isActive }) => isActive ? "nav-select" : ""}
                        >Blog</NavLink>
                    </li>
                    <li>
                        <NavLink 
                            to="/clock" 
                            className={({ isActive }) => isActive ? "nav-select" : ""}
                        >Clock</NavLink>
                    </li>
                    <li>
                        <NavLink 
                            to="/video" 
                            className={({ isActive }) => isActive ? "nav-select" : ""}
                        >Video</NavLink>
                    </li>
                    <li>
                        <NavLink 
                            to="/goals" 
                            className={({ isActive }) => isActive ? "nav-select" : ""}
                        >Goals</NavLink>
                    </li>
                </ul>
            </nav>
        );
    };
    
    export default Nav;
    

    样式不需要修改,保持原来的.main-nav .nav-select即可。如果想简化className的写法,也可以用模板字符串:

    className={({ isActive }) => `nav-item ${isActive ? "nav-select" : ""}`}
    
  • 自定义状态控制的方案(如果需要更灵活的逻辑):
    如果不想用NavLink,可以用useLocation钩子获取当前路由路径,通过React状态来控制类名,避免直接操作DOM:

    import { Link, useLocation } from "react-router-dom";
    
    function Nav() {
        const location = useLocation();
    
        const getNavClass = (path) => {
            return location.pathname === path ? "nav-select" : "";
        };
    
        return (
            <nav className="main-nav">
                <ul>
                    <li>
                        <Link to="/" className={getNavClass("/")}>Home</Link>
                    </li>
                    <li>
                        <Link to="/blog" className={getNavClass("/blog")}>Blog</Link>
                    </li>
                    <li>
                        <Link to="/clock" className={getNavClass("/clock")}>Clock</Link>
                    </li>
                    <li>
                        <Link to="/video" className={getNavClass("/video")}>Video</Link>
                    </li>
                    <li>
                        <Link to="/goals" className={getNavClass("/goals")}>Goals</Link>
                    </li>
                </ul>
            </nav>
        );
    };
    
    export default Nav;
    

    这种方式依赖React的状态更新,能保证路由变化时高亮状态同步,也不会和React的DOM管理冲突。

内容的提问来源于stack exchange,提问作者Niki

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 18:01:20