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

requestAnimationFrame计时器bug:设置winnerPlayerId后无法立即停止

问题分析与解决方案

问题出在你没有清理requestAnimationFrame的循环任务。当winnerPlayerId或turnTimerStartedAt变化时,旧的useEffect实例里的tick函数还在通过requestAnimationFrame持续执行,不断调用setValue,导致你设置的setValue(null)被覆盖,无法立即停止计时器。

要解决这个问题,需要在useEffect的清理函数中取消未完成的动画帧请求,确保每次依赖变化时终止之前的计时器循环。

修改后的代码

useEffect(() => {
  let animationId; // 保存动画帧ID,用于后续取消

  if (turnTimerStartedAt && !winnerPlayerId) {
    const startedAt = Date.now() - (Rune.gameTimeInSeconds() - turnTimerStartedAt) * 1000;

    const tick = () => {
      const newValue = MAX_TURN_TIME - (Date.now() - startedAt) / 1000 - 0.5;
      if (newValue <= 0) {
        setValue(0);
      } else {
        setValue(newValue);
        animationId = requestAnimationFrame(tick); // 保存当前动画帧ID
      }
    };
    animationId = requestAnimationFrame(tick); // 启动第一个动画帧并记录ID
  } else {
    setValue(null);
  }

  // 清理函数:组件卸载或依赖变化时,终止之前的动画循环
  return () => {
    if (animationId) {
      cancelAnimationFrame(animationId);
    }
  };
}, [turnTimerStartedAt, winnerPlayerId]);

关键说明

  • 新增animationId变量存储requestAnimationFrame的返回值,用于追踪当前运行的动画帧任务。
  • useEffect的返回函数会在组件卸载或依赖项变化时执行,调用cancelAnimationFrame(animationId)可以立即终止旧的计时器循环。
  • 当winnerPlayerId被设置后,旧的动画循环会被立刻停止,setValue(null)能直接生效,计时器和动画会马上进入闲置状态。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 14:04:56