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

useCallback中事件监听无法更新React状态的问题排查

解决React事件监听器内修改状态失效及重复触发问题

核心问题:事件监听器内无法修改状态

你的问题根源在于闭包陷阱和事件监听未正确清理:

  1. 闭包导致状态始终为初始值
    原useCallback的依赖数组为空,意味着transition函数只会在组件首次渲染时创建,内部捕获的reset始终是初始状态值。直接用setReset(!reset)时,每次都是基于初始值翻转,看起来状态没有变化。

    必须使用函数式更新,它能获取到最新的状态值,完全不受闭包影响:

    setReset(prevReset => !prevReset);
    
  2. 事件监听未正确移除
    你添加监听时用的是匿名函数,但移除时又创建了新的匿名函数——这两个函数引用不同,导致监听根本无法被移除,残留的监听会干扰状态更新逻辑。

    正确做法是把监听函数抽成独立变量,确保添加和移除用的是同一个函数:

    const transition = useCallback(() => {
      console.log("running callback");
      const content = document.getElementById("content");
      const greeting = document.querySelector(".fadeInAndOut");
      
      // 抽离独立的监听函数
      const handleAnimationEnd = () => {
        console.log("event fired");
        greeting?.classList.remove("fadeInAndOut");
        greeting?.setAttribute("style", "opacity: 0");
        content?.classList.add("fadeIn");
        // 函数式更新状态
        setReset(prev => !prev);
        cleanUp();
        // 移除同一个监听函数
        greeting?.removeEventListener("animationend", handleAnimationEnd);
      };
    
      greeting?.addEventListener("animationend", handleAnimationEnd, { once: true });
    }, []);
    

附加问题:开发环境下事件触发两次

这是React开发环境Strict Mode的特性——它会刻意重复执行useEffect、useCallback等钩子,用来检测潜在的代码问题。你设置了{once: true},但因为transition被执行两次,添加了两个独立的监听函数,所以两个都会触发。

解决办法:

  • 在useEffect中添加清理逻辑,确保组件重渲染时移除之前的监听:
    useEffect(() => {
      const cleanup = transition();
      // 组件卸载/重渲染时执行清理
      return () => cleanup();
    }, []);
    
    // 修改transition函数,返回清理逻辑
    const transition = useCallback(() => {
      const content = document.getElementById("content");
      const greeting = document.querySelector(".fadeInAndOut");
      const handleAnimationEnd = () => {
        // ... 原有逻辑
      };
    
      greeting?.addEventListener("animationend", handleAnimationEnd, { once: true });
      
      // 返回清理函数
      return () => {
        greeting?.removeEventListener("animationend", handleAnimationEnd);
      };
    }, []);
    
  • 生产环境下Strict Mode会被自动禁用,该问题会消失,但开发阶段建议保留Strict Mode,通过正确的清理逻辑规避重复触发。

另外,建议尽量避免直接操作DOM,改用React状态控制类名和样式(比如用状态变量管理fadeInAndOut、fadeIn),能更好地和React的状态机制结合,减少这类DOM操作带来的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 14:25:15