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

useEffect钩子中自定义对象重复初始化且无法销毁的问题排查

问题排查:React中原生JS组件重复初始化且销毁失败

问题场景

  • 原生JS组件TestComponent:构造函数创建每秒触发的定时器,destroy方法负责清除定时器,要求在DOM挂载时初始化、移除时销毁
  • React组件TestApp通过useState存储组件实例,在useEffect中初始化实例并更新状态,同时在清理函数调用destroy
  • 出现问题:组件初始化两次(每秒打印两次"interval is ticking"),且destroy从未执行,清理阶段testComponent状态为null

原因分析

  1. 重复初始化的原因
    React 18+的开发环境默认开启Strict Mode(严格模式),这个模式会刻意重复执行useEffect的初始化逻辑两次,目的是检测组件中是否存在副作用泄漏问题。这就是你看到两次定时器打印的直接原因,生产环境下Strict Mode不会触发该重复执行逻辑。

  2. destroy无法执行的原因
    useEffect的清理函数是通过闭包捕获状态值的。第一次执行useEffect时,testComponent还是初始的null,此时注册的清理函数引用的就是这个null;当调用setTestComponent更新状态后,闭包已改变,但旧的清理函数不会同步更新,因此组件卸载或依赖变化触发清理时,本质是调用null.destroy(),自然不会执行销毁逻辑。

修复方案

1. 解决开发环境重复初始化

如果只是开发环境的问题,生产环境不会出现,可直接忽略;若想在开发环境避免该情况,删除根组件外层的<StrictMode>标签即可。

2. 修复destroy不执行的问题

用useRef替代useState存储组件实例,useRef的current属性是可变的,不受闭包限制,清理函数能直接获取最新实例:

import { useEffect, useRef } from 'react';

function TestApp() {
  const testComponentRef = useRef(null);

  useEffect(() => {
    // 初始化原生组件实例
    testComponentRef.current = new TestComponent();
    // 清理函数:销毁实例
    return () => {
      testComponentRef.current?.destroy();
    };
  }, []); // 空依赖数组确保只执行一次(生产环境)

  return <div>Test App</div>;
}

即便在Strict Mode下,React会先执行第一次初始化的清理函数销毁第一个实例,再执行第二次初始化创建新实例,不会出现定时器泄漏问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 14:20:06