Rxjs 为什么mapTo只更改一次?
我正在制作秒表,当我想第二次重置时钟时,它不会改变。 第一次单击时,它会设置h:0、m:0、s:0。但当再次单击时,它不会设置h:0、m:0、s:0,秒表将继续Rxjs 为什么mapTo只更改一次?,rxjs,rxjs-observables,rxjs-pipeable-operators,Rxjs,Rxjs Observables,Rxjs Pipeable Operators,我正在制作秒表,当我想第二次重置时钟时,它不会改变。 第一次单击时,它会设置h:0、m:0、s:0。但当再次单击时,它不会设置h:0、m:0、s:0,秒表将继续 const events$ = merge( fromEvent(startBtn, 'click').pipe(mapTo({count: true})), click$.pipe(mapTo({count: false})), fromEvent(resetBtn, 'click').pipe(mapTo(
const events$ = merge(
fromEvent(startBtn, 'click').pipe(mapTo({count: true})),
click$.pipe(mapTo({count: false})),
fromEvent(resetBtn, 'click').pipe(mapTo({time: {h: 0, m: 0, s: 0}})) // there is reseting
)
const stopWatch$ = events$.pipe(
startWith({count: false, time: {h: 0, m: 0, s: 0}}),
scan((state, curr) => (Object.assign(Object.assign({}, state), curr)), {}),
switchMap((state) => state.count
? interval(1000)
.pipe(
tap(_ => {
if (state.time.s > 59) {
state.time.s = 0
state.time.m++
}
if (state.time.s > 59) {
state.time.s = 0
state.time.h++
}
const {h, m, s} = state.time
secondsField.innerHTML = s + 1
minuitesField.innerHTML = m
hours.innerHTML = h
state.time.s++
}),
)
: EMPTY)
stopWatch$.subscribe()
问题
您正在使用可变状态并将其更新为observable发出的事件的副作用(tap就是这样做的)
一般来说,产生副作用间接改变产生副作用的流是个坏主意。因此,创建日志或显示值不太可能引起问题,但改变对象然后将其注入流中很难维护/缩放
一种修复方法:
创建一个新对象
//fromEvent(resetBtn,'click').pipe(映射到({time:{h:0,m:0,s:0}))
fromEvent(resetBtn,'click').pipe(映射({time:{h:0,m:0,s:0})))
尽管应该是一个创可贴解决方案,但这应该奏效。
预加工解决方案
这是我刚才做的秒表。下面是它的工作原理。通过给秒表一个control$
observable来创建秒表(在本例中,我使用了一个名为controller
的主题)
当控制$
发出“启动”
时,秒表启动;当它发出“停止”
时,秒表停止;当它发出“重置”
时,秒表将计数器设置回零。当control$
出错、完成或发出“END”
时,秒表会出错或完成
函数createStopwatch(控件$:可观察,间隔=1000):可观察{
返回延迟(()=>{
let toggle:boolean=false;
让计数:数字=0;
常量代码=()=>{
返回计时器(0,间隔)。管道(
映射(x=>count++)
)
}
返回控制$.pipe(
catchError(=>of(“END”)),
s=>concat(s,of(“END”)),
过滤器(控制=>
控件==“开始”||
控件==“停止”||
控件==“重置”||
控件==“结束”
),
开关映射(控件=>{
如果(控件==“开始”&&&!切换){
切换=真;
返回ticker();
}else if(控制==“停止”&切换){
切换=假;
返回空;
}否则如果(控制==“重置”){
计数=0;
如果(切换){
返回ticker();
}
}
返回空;
})
);
});
}
//适合您的代码:)
const controller=新主题();
const seconds$=创建秒表(控制器);
fromEvent(startBtn,'click').pipe(映射到(“开始”)).subscribe(控制器);
fromEvent(resetBtn,'click').pipe(映射到(“重置”)).subscribe(控制器);
秒$.subscribe(秒=>{
secondsField.innerHTML=秒%60;
minutesfield.innerHTML=数学地板(秒/60)%60;
hours.innerHTML=Math.floor(秒/3600);
});
作为奖励,您可能可以看到如何在不重置计时器的情况下制作一个按钮,使停止
此计时器
没有主题
这里有一种更惯用的反应方式。它通过直接合并DOM事件(中间没有主题)为秒表生成一个控件$
这确实会剥夺您编写类似于controller.next(“重置”)
将您自己的值随意注入流中或控制器。完成()代码>当应用程序使用秒表完成时(尽管您可能会通过其他事件自动完成)
。。。
//适合您的代码:)
创建秒表(合并)(
fromEvent(开始,单击)。管道(映射到(“开始”),
fromEvent(resetBtn,'click')。管道(映射到(“重置”))
)).订阅(秒=>{
secondsField.innerHTML=秒%60;
minutesfield.innerHTML=数学地板(秒/60)%60;
hours.innerHTML=Math.floor(秒/3600);
});
我怀疑发生这种情况,因为每次调用mapTo()
时都使用相同的对象实例。尝试使用map(()=>({time:{h:0,m:0,s:0}}))
instad.@martin它很有效,谢谢你,非常感谢:)