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

React回调传入TypeScript单例后setState失效问题排查

问题根源:单例持有的是旧回调的闭包

Daemon是全局单例,一旦你把updateTime传给getInstance,它就会一直攥着这个回调的引用不放。但React组件每次渲染(不管是状态变化还是props更新)都会重新生成updateTime函数,单例里存的却始终是第一次传进去的旧版本。

这个旧的updateTime通过闭包捕获的是组件第一次渲染时的updateSimTime和状态上下文,等组件后续重新渲染后,这个旧回调里的updateSimTime根本管不了当前组件的状态——它只认当初那个渲染周期的状态,自然触发不了更新。你可以试试在updateTime里打印当前的simTime,肯定显示的是初始值,这就是闭包在搞鬼。

解决办法

办法1:用useCallback+useEffect同步单例回调

把updateTime用useCallback包裹,确保只有updateSimTime变化时才重新生成函数,然后每次组件渲染都把新的回调同步给单例:

const updateTime = useCallback((time: string) => {
  updateSimTime(time);
  console.log(time);
}, [updateSimTime]);

useEffect(() => {
  const daemon = Daemon.getInstance(updateTime);
  // 组件卸载时可清理回调,避免内存泄漏(如果Daemon有对应方法的话)
  return () => {
    // daemon.clearCallback();
  };
}, [updateTime]);

办法2:给Daemon添加更新回调的方法

修改Daemon类,让它支持动态替换回调,这样组件能主动把新的updateTime传给它:

// Daemon.ts
class Daemon {
  private static instance: Daemon;
  private callback: (time: string) => void;

  private constructor(callback: (time: string) => void) {
    this.callback = callback;
    setInterval(() => this.tick(), 1000);
  }

  public static getInstance(callback?: (time: string) => void): Daemon {
    if (!Daemon.instance) {
      if (!callback) throw new Error("首次初始化必须传回调");
      Daemon.instance = new Daemon(callback);
    }
    return Daemon.instance;
  }

  // 新增:动态更新回调的方法
  public updateCallback(newCallback: (time: string) => void): void {
    this.callback = newCallback;
  }

  private tick(): void {
    const currentTime = new Date().toISOString();
    this.callback(currentTime);
  }
}

然后在组件中同步最新回调:

const updateTime = useCallback((time: string) => {
  updateSimTime(time);
  console.log(time);
}, [updateSimTime]);

useEffect(() => {
  const daemon = Daemon.getInstance();
  daemon.updateCallback(updateTime);
}, [updateTime]);

办法3:用useRef存储最新的setter

把updateSimTime放进useRef里,让旧回调也能访问到最新的状态设置函数:

const updateSimTimeRef = useRef<typeof updateSimTime>(updateSimTime);

// 每次updateSimTime更新时同步到ref
useEffect(() => {
  updateSimTimeRef.current = updateSimTime;
}, [updateSimTime]);

const updateTime = useCallback((time: string) => {
  updateSimTimeRef.current(time);
  console.log(time);
}, []);

// 初始化单例
useEffect(() => {
  Daemon.getInstance(updateTime);
}, [updateTime]);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 03:21:02