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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 20:54:40