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

Redux状态更新后在requestAnimationFrame内无法获取最新值问题求助

问题根因

这是典型的React闭包陷阱问题:

  • 你的animate函数在组件首次渲染时创建,捕获了当时的backgroundMode初始值None
  • 后续Redux状态更新触发组件重渲染,会生成新的animate函数,捕获最新的backgroundMode,但你当前的useEffect只在bodyPixNet变化时执行,一直调用的是首次创建的旧animate函数,旧函数永远持有初始值,所以打印结果不会更新
  • 你直接把backgroundMode加入依赖后,每次状态变化都会重新执行useEffect,如果旧动画帧没有被正确及时取消,就会出现多动画线程并行的问题,尤其是你的animate是异步函数,内部异步逻辑可能导致cancelAnimationFrame执行时,新的帧已经被调度。

推荐解决方案

方案1:用useRef存储最新状态(改造成本最低,无需改动动画逻辑)

用ref持久化存储最新的backgroundMode值,ref的引用是稳定的,无论闭包何时执行,都能拿到最新的current值:

const VideoCanvas = ({ videoRef }: IVideoCanvasProps) => {
  // 原有从Redux取值逻辑保持不变
  const backgroundMode = useSelector(/* 你的选择器逻辑 */);
  // 新增ref存储最新值
  const backgroundModeRef = useRef(backgroundMode);
  // 每次backgroundMode更新时同步到ref
  useEffect(() => {
    backgroundModeRef.current = backgroundMode;
  }, [backgroundMode]);

  async function animate() {
    ...
    // 这里换成读ref的current,不要直接读backgroundMode
    console.log("animate backgroundMode", backgroundModeRef.current);
    ...
    animationFrame.current = requestAnimationFrame(animate);
  }

  // 原来的useEffect完全不用改,也不会出现多线程问题
  useEffect(() => {
    animationFrame.current = requestAnimationFrame(animate);
    return () => {
      if (animationFrame.current) {
        cancelAnimationFrame(animationFrame.current);
      }
    };
  }, [bodyPixNet]);
}

方案2:直接从Redux Store读取最新值

如果你能在组件内拿到Redux的store实例,可以直接在animate函数中调用getState获取实时状态,完全避开闭包捕获问题:

// 假设你已经在文件中导入了项目的store实例
async function animate() {
  ...
  const latestBackgroundMode = store.getState().你的状态模块名.backgroundMode;
  console.log("animate backgroundMode", latestBackgroundMode);
  ...
}

方案3:修复依赖数组的实现(不推荐,需要调整异步逻辑)

如果你一定要把backgroundMode加入依赖,需要增加运行状态判断,避免animate的异步逻辑在清理后还调度新的帧:

useEffect(() => {
  let isActive = true;
  async function animate() {
    if (!isActive) return;
    // 原有逻辑保持不变
    ...
    if (isActive) {
      animationFrame.current = requestAnimationFrame(animate);
    }
  }
  animationFrame.current = requestAnimationFrame(animate);
  return () => {
    isActive = false;
    if (animationFrame.current) {
      cancelAnimationFrame(animationFrame.current);
    }
  };
}, [bodyPixNet, backgroundMode]);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 02:54:04