PAYATHON 2026

React Native 倒计时结束后为何无法重置?

数据阿明

结论

从给出的代码看,重置函数会把 millis 设为 minutesToMillis(minutes)。需要调整的是 countDown:它在 setMillis 的状态更新函数中调用了 onEnd,而 onEnd 还会更新其他状态并重置计时器。状态更新函数应保持纯粹。将结束处理移到状态更新之外,可以避免重渲染或开发模式下重复执行更新函数时触发副作用。

这里 minutes 的初始值是 0.1,minutesToMillis(0.1) 得到 6000 毫秒,也就是 6 秒。App.js 传入的 onTimerEnd={() => {}} 与这里的重置路径无关,因为贴出的 Timer 组件没有接收或使用这个属性。由于 Timer.js 片段不完整,下面的调整方式依据所示的 Countdown 与 onEnd 调用关系。

将归零处理放到状态更新之外

在 Countdown 中,让间隔回调只更新 millis。状态变为 0 时,再通过单独的 effect 调用结束回调。可以用 ref 避免归零期间重复触发:

const interval = React.useRef(null);
const hasEnded = React.useRef(false);
const [millis, setMillis] = useState(null);

const reset = React.useCallback(() => {
  setMillis(minutesToMillis(minutes));
}, [minutes]);

const countDown = () => {
  setMillis((time) => {
    if (time == null || time <= 1000) return 0;
    return time - 1000;
  });
};

useEffect(() => {
  if (millis === 0 && !isPaused && !hasEnded.current) {
    hasEnded.current = true;
    clearInterval(interval.current);
    onEnd(reset);
  }

  if (millis > 0) {
    hasEnded.current = false;
  }
}, [millis, isPaused, onEnd, reset]);

保留现有的初始化 effect 和计时器 effect。计时器 effect 仍可根据 isPaused 启停间隔:

useEffect(() => {
  if (isPaused) {
    if (interval.current) clearInterval(interval.current);
    return;
  }

  interval.current = setInterval(countDown, 1000);
  return () => clearInterval(interval.current);
}, [isPaused]);

millis 变为 0 时,结束 effect 会停止间隔并调用 onEnd(reset)。随后,Timer 中的 onEnd 会停止计时、恢复进度并调用 reset(),让显示回到 6 秒。

检查重置时长和组件传参

确认 minutes 是预期时长,并且 Countdown 实际收到这个值。若要避免默认值与父组件状态不一致,可以在 Timer 中显式传入 minutes:

<Countdown
  minutes={minutes}
  isPaused={!isStarted}
  onProgress={setProgress}
  onEnd={onEnd}
/>

当前片段中,Timer 用 useState(0.1) 设置了 minutes。如果没有其他地方修改它,重置值就是 6 秒。Countdown 自身的默认值也是 0.1,但父组件传入的 minutes 会覆盖默认值。

还要确认倒计时组件卸载时会清理间隔,并且结束时的 onEnd 确实调用了 reset。如果按上述方式调整后仍停在 00:00,检查未展示的 JSX 是否将 onEnd 传给了正确的 Countdown 实例,以及计时结束时组件是否被卸载或替换。

备注:内容仅供参考。