如何使用RxJS操作符实现自定义定时器暂停并从上次记录值继续计时
解决方案
你可以结合RxJS的scan、takeUntil和switchMap操作符实现暂停继续的需求,核心思路是单独维护累计计时值,暂停时终止当前interval流,下次启动时基于上次保存的累计值继续累加计数,修改后的完整代码如下:
import { useState, useEffect } from 'react'; import { interval, Subject } from 'rxjs'; import { startWith, switchMap, takeUntil, map } from 'rxjs/operators'; const actionReset$ = new Subject(); const actionWait$ = new Subject(); function CustomTimer() { const [time, setTime] = useState(0); const [timerOn, setTimerOn] = useState(false); // 存储暂停时的计时值 const [pausedTime, setPausedTime] = useState(0); useEffect(() => { let subscription; if (timerOn) { subscription = actionReset$.pipe( startWith(null), switchMap(() => { // 重置时清空暂停保存的数值 setPausedTime(0); // 新的interval从0开始计数,加上之前暂停的数值得到当前总时间 return interval(1000).pipe( map(sec => sec + 1 + pausedTime), // 点击wait时终止当前interval流 takeUntil(actionWait$) ); }) ).subscribe(currentTime => { setTime(currentTime); }); } return () => subscription?.unsubscribe(); }, [timerOn, pausedTime]); // wait按钮点击回调 const handleWait = () => { setTimerOn(false); // 保存当前的计时值,下次启动接着累加 setPausedTime(time); actionWait$.next(); }; // reset回调 const handleReset = () => { setTime(0); setPausedTime(0); actionReset$.next(); }; // 补全原代码缺失的stop点击回调 const onStopClick = () => { setTimerOn(false); setTime(0); setPausedTime(0); } return ( <div className="mainBlock"> <h2>{time}秒</h2> {!timerOn && ( <button onClick={() => setTimerOn(true)}>Start</button> )} {timerOn && ( <button onClick={onStopClick}>Stop</button> )} {timerOn && ( <button onClick={handleWait}>Wait</button> )} {time > 0 && ( <button onClick={handleReset}>Reset</button> )} </div> ); } export default CustomTimer;
核心修改点说明
- 新增
pausedTime状态存储暂停时的计时值,下次启动interval时直接在新的计数基础上叠加该值,实现从暂停位置继续计时 - 用
takeUntil(actionWait$)操作符,点击wait按钮时自动终止当前运行的interval流,停止计数 - 调整interval的映射逻辑,每次输出的计数值自动叠加之前暂停保存的数值,不需要额外修改计数逻辑
- 补全了原代码中缺失的
onStopClick回调逻辑,保证停止按钮功能正常
内容的提问来源于stack exchange,提问作者Александр Соколов
相关产品推荐
相关产品推荐

