React绑定addEventListener报Cannot read properties of null错怎么办
问题原因
报错的核心原因是组件执行渲染逻辑时,你写的<div id='wrapper'>还没有被挂载到真实DOM树中,此时调用document.getElementById('wrapper')拿到的返回值是null,对null调用addEventListener自然会抛出类型错误。
除此之外你当前的写法还有两个隐藏bug:
- 组件每次触发重渲染都会重复执行一次事件绑定逻辑,会导致监听器重复叠加,触发多次回调
- 手动绑定的原生事件脱离React生命周期管控,组件卸载时如果不主动清理,会造成内存泄漏
解决方案
你要求的「仅在外层容器绑定单个监听器实现事件委托」,React的合成事件系统本身就天然支持,完全不需要手动操作DOM绑定原生事件,直接给外层容器传入onClick回调即可,完全符合你的规则约束:
import { useNavigate } from 'react-router-dom'; const ButtonContainer = ({ users }) => { const navigate = useNavigate(); const handleWrapperClick = (event) => { // 拦截非按钮元素的点击事件 if (event.target.nodeName !== 'BUTTON') { return; } // 读取按钮上的id参数完成跳转 navigate(`/singleuser/${event.target.id}`); } return ( <div id="wrapper" onClick={handleWrapperClick}> {users.map((user, idx) => ( <button key={idx} id={user.id ?? idx}> User {idx + 1} </button> ))} </div> ) } export default ButtonContainer
补充说明:React的合成事件默认采用事件委托机制,所有事件监听统一挂载在应用根节点上,你给外层wrapper绑定的onClick本身就只会注册单个监听器,完全满足「不给每个按钮单独绑定监听器」的要求,不需要额外手写原生addEventListener逻辑。
如果你受特殊规则限制必须手动绑定原生DOM事件,也需要通过useRef获取DOM节点,在useEffect钩子中完成绑定,同时在组件卸载时主动清理监听器:
import { useNavigate } from 'react-router-dom'; import { useEffect, useRef } from 'react'; const ButtonContainer = ({ users }) => { const navigate = useNavigate(); const wrapperRef = useRef(null); useEffect(() => { const wrapper = wrapperRef.current; const handleClick = (event) => { if (event.target.nodeName !== 'BUTTON') return; navigate(`/singleuser/${event.target.id}`); } wrapper.addEventListener('click', handleClick); // 组件卸载时移除监听器,避免内存泄漏 return () => wrapper.removeEventListener('click', handleClick); }, [navigate]) return ( <div ref={wrapperRef} id="wrapper"> {users.map((user, idx) => ( <button key={idx} id={user.id ?? idx}> User {idx + 1} </button> ))} </div> ) } export default ButtonContainer
内容的提问来源于stack exchange,提问作者Vatsal A Mehta
相关产品推荐
相关产品推荐

