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

React 18升级切换组件时出现removeEventListener空值读取错误

React 16.12 升级到 18 后组件切换出现 removeEventListener 空值错误排查

问题概述

将React代码库从v16.12升级到v18后,切换组件时触发以下错误:

Uncaught TypeError: Cannot read properties of null (reading 'removeEventListener')
    at eval (index.js:381:295102)
    at safelyCallDestroy (react-dom.development.js:22927:5)
    at commitHookEffectListUnmount (react-dom.development.js:23095:11)
    at commitPassiveUnmountInsideDeletedTreeOnFiber (react-dom.development.js:25093:11)
    at commitPassiveUnmountEffectsInsideOfDeletedTree_begin (react-dom.development.js:25043:5)
    at commitPassiveUnmountEffects_begin (react-dom.development.js:24951:11)
    at commitPassiveUnmountEffects (react-dom.development.js:24936:3)
    at flushPassiveEffectsImpl (react-dom.development.js:27033:3)
    at flushPassiveEffects (react-dom.development.js:26979:14)
    at commitRootImpl (react-dom.development.js:26930:5)

错误指向打包后index.js第381行的useEffect清理函数:

Object(De.useEffect)(function() {
    if (H(),
    null !== E.current)
        return E.current.addEventListener("scroll", t),
        window.addEventListener("scroll", n),
        window.addEventListener("resize", t),
        window.addEventListener("resize", n),
        window.addEventListener("resize", H),
        function() {
            E.current.removeEventListener("scroll", t),
            window.removeEventListener("scroll", n),
            window.removeEventListener("resize", t),
            window.removeEventListener("resize", n),
            window.removeEventListener("resize", H)
        }
}, []),

已尝试操作

  • 在清理函数中添加空值判断,无效
  • 注释所有removeEventListener代码,错误依旧出现
  • 升级webpack和webpack-cli到最新版本,编译显示成功但问题未解决:
./src/components/Audiences/Filters/Index.js 9.46 KiB [built] [code generated]
webpack 5.88.2 compiled successfully in 1175 ms

解决方向与排查建议

1. 确认代码修改是否真的被编译生效

  • 强制刷新浏览器缓存:使用Ctrl+Shift+R(Windows/Linux)或Cmd+Shift+R(Mac)强制刷新,避免浏览器加载旧的打包文件
  • 检查打包后的代码:直接查看dist/index.js对应行的内容,确认你的修改(比如注释清理函数)是否被编译进去。如果没生效,排查:
    • 确认修改的文件路径是否正确,是否是webpack打包的入口文件依赖的组件
    • 检查webpack配置中的cache选项,若开启了持久化缓存,删除node_modules/.cache或webpack指定的缓存目录后重新打包
    • 检查webpack devServer的watch配置,确保监听了src目录下的文件变化

2. 适配React 18的useEffect行为变化

React 18在开发环境严格模式下会模拟组件的挂载-卸载-重新挂载流程,双重调用useEffect的挂载和清理函数,可能导致清理时ref.current已变为null。正确的处理方式:

  • 在清理函数中也添加空值判断:
    useEffect(() => {
      if (filtersContainerRef.current !== null) {
        filtersContainerRef.current.addEventListener('scroll', removeActiveFilter);
        window.addEventListener('scroll', setFiltersContainerOffsetTop);
        window.addEventListener('resize', removeActiveFilter);
        window.addEventListener('resize', setFiltersContainerOffsetTop);
      }
    
      return () => {
        // 清理DOM元素监听前先判断ref是否存在
        if (filtersContainerRef.current) {
          filtersContainerRef.current.removeEventListener('scroll', removeActiveFilter);
        }
        // window监听无需判断,直接移除
        window.removeEventListener('scroll', setFiltersContainerOffsetTop);
        window.removeEventListener('resize', removeActiveFilter);
        window.removeEventListener('resize', setFiltersContainerOffsetTop);
      };
    }, []);
    
  • 确保监听的回调函数是稳定的:如果removeActiveFilter或setFiltersContainerOffsetTop是组件内定义的函数,用useCallback包裹,避免每次渲染创建新函数导致清理时无法匹配到正确的监听函数:
    const removeActiveFilter = useCallback(() => {
      // 函数逻辑
    }, []);
    
    const setFiltersContainerOffsetTop = useCallback(() => {
      // 函数逻辑
    }, []);
    

3. 排查依赖冲突

  • 检查项目中是否存在多个版本的React/ReactDOM:执行npm ls react react-dom,如果有多个版本,清理旧版本后重新安装
  • 检查第三方组件库是否支持React 18:如果错误代码来自第三方组件,升级该组件到支持React 18的版本

4. 其他排查手段

  • 开启React严格模式:在根组件包裹<StrictMode>,查看控制台是否有其他警告信息,帮助定位潜在问题
  • 使用React DevTools:查看组件的挂载/卸载流程,确认清理函数执行时的组件状态和ref值

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 08:35:41