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
相关产品推荐
相关产品推荐

