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 实例,以及计时结束时组件是否被卸载或替换。
备注:内容仅供参考。
