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

React新手Navbar报错:Uncaught TypeError: Cannot read properties of null

问题:React汉堡菜单报错"Uncaught TypeError: Cannot read properties of null (reading 'addEventListener')"

报错信息:

Uncaught TypeError: Cannot read properties of null (reading 'addEventListener')

报错触发于执行menu.addEventListener时,根源是const menu = document.querySelector('.menu_button')返回了null。

错误原因

  • React组件执行逻辑是先运行函数体内代码,再将JSX渲染为真实DOM。你直接在组件顶层调用document.querySelector时,目标DOM元素还未生成,自然找不到.menu_button,返回null后调用addEventListener就会报错。
  • 这种直接操作DOM的写法不符合React状态驱动的设计理念,容易引发生命周期相关的DOM操作问题。

解决方法

方法1:使用React状态+内置事件绑定(推荐)

完全采用React的状态管理实现,避免直接操作DOM,通过状态控制类名切换:

修改后的完整组件代码:

import { useState } from 'react';
import Toggle from "../Toggle/Toggle";
import "./Navbar.css";
import {Link} from 'react-scroll'

const Navbar = () => {
  // 用状态控制菜单展开/收起状态
  const [menuOpen, setMenuOpen] = useState(false);

  return (
    <div className="n-wrapper" id="Navbar">
      {/* left */}
      <div className="n-left">
        <div className="n-name">Barr Ify</div>
        <Toggle />
      </div>
      {/* right */}
      <div className="n-right">
        <div className="n-list">
          <ul>
            <Link activeClass="active" to="Navbar" spy={true} smooth={true}>
              <li>Home</li>
            </Link>
            <Link to="services" spy={true} smooth={true}>
              <li>Services</li>
            </Link>
            <Link to="experience" spy={true} smooth={true}>
              <li>Experience</li>
            </Link>
            <Link to="portfolio" spy={true} smooth={true}>
              <li>Portfolio</li>
            </Link>
            <Link to="testimonial" spy={true} smooth={true}>
              <li>Testimonial</li>
            </Link>
            <div className="menu-wrapper">
              {/* 绑定点击事件,切换状态并动态控制类名 */}
              <div 
                className={`menu_button ${menuOpen ? 'close' : ''}`}
                onClick={() => setMenuOpen(!menuOpen)}
              >
                <div className="bars"></div>
              </div>
            </div>
          </ul>
        </div>

        {/* Mobile Nav - 根据状态动态控制类名 */}
        <div className={`mobile-nav ${menuOpen ? 'open' : ''}`}>
          <ul className="mobile-lists">
            <Link activeClass="active" to="Navbar" spy={true} smooth={true}>
              <li>Home</li>
            </Link>
            <Link to="services" spy={true} smooth={true}>
              <li>Services</li>
            </Link>
            <Link to="experience" spy={true} smooth={true}>
              <li>Experience</li>
            </Link>
            <Link to="portfolio" spy={true} smooth={true}>
              <li>Portfolio</li>
            </Link>
            <Link to="testimonial" spy={true} smooth={true}>
              <li>Testimonial</li>
            </Link>
          </ul>
          <Link to="contact" spy={true} smooth={true}>
            <button className="button mobile-nav-button">Contact</button>
          </Link>
        </div>

        <Link to="contact" spy={true} smooth={true}>
          <button className="button n-button">Contact</button>
        </Link>
      </div>
    </div>
  );
};

export default Navbar;

方法2:使用useEffect钩子(兼容原生DOM操作)

如果坚持使用原生DOM操作,需要将DOM查询和事件监听放到useEffect中,确保DOM渲染完成后执行:

修改后的组件核心代码片段:

import { useEffect } from 'react';
import Toggle from "../Toggle/Toggle";
import "./Navbar.css";
import {Link} from 'react-scroll'

const Navbar = () => {
  useEffect(() => {
    const menu = document.querySelector('.menu_button');
    const showMenu = document.querySelector('.mobile-nav');
    let menuOpen = false;

    // 先判断元素是否存在,避免空值报错
    if (menu && showMenu) {
      const handleMenuClick = () => {
        if(!menuOpen){
          menu.classList.add('close');
          showMenu.classList.add('open');
          menuOpen= true;
        } else {
          menu.classList.remove('close');
          showMenu.classList.remove('open');
          menuOpen = false;
        }
      }
      menu.addEventListener('click', handleMenuClick);
      
      // 组件卸载时移除事件监听,避免内存泄漏
      return () => menu.removeEventListener('click', handleMenuClick);
    }
  }, []); // 空依赖数组确保只在组件挂载时执行一次

  // 原返回JSX部分保持不变
  return (
    // ... 你的JSX代码
  );
};

export default Navbar;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 18:47:16