尝试同时学习和使用RxJS,证明它非常粗糙!
以下是我的要求 - 我有一个Spinner组件,从isLoading
道具设置为true
开始:
isLoading
在false
之前设置为initialDelay
,则永远不会显示isLoading
在false
之后但在initialDelay
之前设置为minSpinTime
,则会minSpinTime
继续isLoading
然后消失false
在initialDelay
之后和minSpinTime
之后设置为isLoading
,则会显示然后与Subject
为了实现这一点,我有一个Subject
根据我的UI传递一个布尔值。但是我想将一些运算符应用于subject
.audit(
(val: boolean) =>
val ? Rx.Observable.interval(initialDelay) : Rx.Observable.interval(0)
)
// doesnt work, false is merely delayed, i want it synchronous
.audit(
(val: boolean) =>
val ? Rx.Observable.interval(0) : Rx.Observable.interval(minSpinTime)
)
// this also doesnt work, false goes thru too fast
// .switchMap(
// (val: boolean) =>
// val
// ? Rx.Observable.of(true)
// : Rx.Observable.of(false).throttleTime(minSpinTime) //auditTime also doesnt work here
// )
.subscribe((val: boolean) => this.setState({ show: val }));
,但我完全迷失了要使用的运算符。这就是我现在所拥有的(订阅代码使用React但这里不相关):
xstate
我觉得我需要咨询更有经验的RxJS人。请帮忙!我想我可能需要结合两个或更多的可观察量来达到预期的效果。我知道我需要至少有一个这样的observable在第一个之后开始,所以建议使用switchMap?
编辑1:这是一个半工作的代码盒:https://codesandbox.io/s/72k9qko211但它不符合第3规范的“同步消失”要求
编辑2:我实际上有一个关于这个组件的完整工作示例:http://tsiq-ui-components.s3-website-us-east-1.amazonaws.com/?knob-isLoading=true&knob-initialDelay=3000&knob-minSpinTime=3000&selectedKind=Components%2FIcons&selectedStory=3000ms%20SmartSpinner&full=0&addons=1&stories=1&panelRight=0&addonPanel=storybooks%2Fstorybook-addon-knobs但它是用@ DavidKPiano的RxJS
实现的,我试图将其转换为使用a
- 但是你可以看到它符合规范中的所有3个案例(toggle isLoading)。希望沟通清晰
答案 0 :(得分:1)
我使用了两个不同的Subject
,因为触发加载/未加载与发出加载状态的延迟流之间存在关系。
基本上,我做了两个延迟了一些量的流,如果在延迟之前触发了另一个主题,就会中止。
zip
运算符用于确保在调用deactivateLoaderSubject并且都经过最小延迟时发生停用。
SwitchLatest
将使外部流保持活动状态,而内部流仅发出最大一次。
这个解决方案唯一没有考虑到的是,如果你想在之前立即从deactivateLoaderStream
发出
initialDelay
。我无法弄清楚为什么会出现这个问题,所以我选择了最简单的解决方案。
代码框在这里:https://codesandbox.io/s/mq380ol5nj
在发布此答案时,代码如下所示:
import React from "react";
import { render } from "react-dom";
import Rx from "rxjs";
const initialDelay = 1000;
const minSpinTime = 1000;
const activateLoaderSubject = new Rx.Subject();
const deactivateLoaderSubject = new Rx.Subject();
const activateLoaderStream = activateLoaderSubject.switchMap(() =>
Rx.Observable.of(null)
.delay(initialDelay)
.takeUntil(deactivateLoaderSubject)
);
const deactivateLoaderStream = activateLoaderSubject.switchMap(() =>
Rx.Observable.zip(
Rx.Observable.of(null)
.delay(initialDelay + minSpinTime)
.takeUntil(activateLoaderSubject),
deactivateLoaderSubject
).take(1)
);
const initialState = { loading: null };
const activateLoaderReducerStream = activateLoaderStream.map(() => state => ({
...state,
loading: true
}));
const deactivateLoaderReducerStream = deactivateLoaderStream.map(
() => state => ({ ...state, loading: false })
);
const stateStream = Rx.Observable.merge(
activateLoaderReducerStream,
deactivateLoaderReducerStream
)
.startWith(initialState)
.scan((state, reducer) => reducer(state));
stateStream.forEach(state => {
render(
<div>{JSON.stringify(state, null, 2)}</div>,
document.getElementById("root")
);
});
// Removes loader directly when deactivate is triggered
const finnishVeryLate = () => {
activateLoaderSubject.next();
setTimeout(() => {
deactivateLoaderSubject.next();
}, initialDelay + minSpinTime + 1000);
};
// Shows loader for minSpinTime
const finnishLate = () => {
activateLoaderSubject.next();
setTimeout(() => {
deactivateLoaderSubject.next();
}, initialDelay + 1);
};
// Doesn't show loader
const finnishEarly = () => {
activateLoaderSubject.next();
setTimeout(() => {
deactivateLoaderSubject.next();
}, initialDelay - 1);
};