
今天我們來看看如何改變事件發(fā)生的頻率。讓我們添加兩個(gè)按鈕,分別代表了二分之一秒產(chǎn)生一個(gè)事件的事件流和四分之一秒產(chǎn)生一個(gè)事件的事件流。根據(jù)昨天的文章,我們可以想到用 merge 操作符把它們和開始按鈕合并起來。
const time$ = merge(startBtnClick$, halfBtnClick$, quarterBtnClick$);
const subscription = time$
.pipe(
switchMapTo(addOneOrReset$),
startWith({ count: 0 }),
scan((acc, current) => current(acc))
)
很明顯,這三個(gè)按鈕實(shí)現(xiàn)的效果是一樣的,還是每隔一秒產(chǎn)生一個(gè)事件。既然我們要修改事件產(chǎn)生的頻率,那么我們就要知道原來這個(gè)事件流是怎么定義的:
const perSecond$ = interval(1000);
我們看到是由 interval 的參數(shù)來控制產(chǎn)生事件的頻率,來修改一下代碼:
const time$ = merge(
startBtnClick$.pipe(mapTo(1000)),
halfBtnClick$.pipe(mapTo(500)),
quarterBtnClick$.pipe(mapTo(250))
);
這樣就行了嗎?開玩笑,當(dāng)然不行了,這只是把三個(gè)按鈕的 click 事件產(chǎn)生的值做了修改。我們需要做的是修改傳給 interval 的參數(shù)。現(xiàn)在的代碼是這樣的:
const perSecond$ = interval(1000);
const intervalCanBeStopped$ = perSecond$.pipe(takeUntil(pauseBtnClick$));
const addOneOrReset$ = merge(
intervalCanBeStopped$.pipe(mapTo(addOne)),
resetBtnClick$.pipe(mapTo(reset))
);
const time$ = merge(
startBtnClick$.pipe(mapTo(1000)),
halfBtnClick$.pipe(mapTo(500)),
quarterBtnClick$.pipe(mapTo(250))
);
const subscription = time$
.pipe(
switchMapTo(addOneOrReset$),
startWith({ count: 0 }),
scan((acc, current) => current(acc))
)
.subscribe(v => setTxt(v.count));
首先,我們得知,三個(gè)按鈕點(diǎn)擊后的事件流轉(zhuǎn)到了 switchMapTo 操作符;其次,我們知道 addOneOrReset$ 是由 interval(1000) 組合而來的。也就是說,我們需要把流轉(zhuǎn)到 switchMapTo 的事件傳遞給 addOneOrReset$。這里要用到了 switchMapTo 的兄弟,switchMap 操作符。
switchMap:參數(shù)為函數(shù),這個(gè)函數(shù)接收事件流中的事件作為參數(shù),返回值為另一個(gè)事件流。
讓我們一步一步來修改代碼,首先把時(shí)間參數(shù)傳遞給 switchMap 的函數(shù)參數(shù):
const subscription = time$
.pipe(
switchMap((time) => addOneOrReset$),
startWith({ count: 0 }),
scan((acc, current) => current(acc))
)
.subscribe(v => setTxt(v.count));
其次,我們需要把 addOneOrReset$ 拆開找到 interval 操作符,把 time 傳遞給它:
const subscription = time$
.pipe(
switchMap(time =>
merge(
interval(time).pipe(
takeUntil(pauseBtnClick$),
mapTo(addOne)
),
resetBtnClick$.pipe(mapTo(reset))
)
),
startWith({ count: 0 }),
scan((acc, current) => current(acc))
)
.subscribe(v => setTxt(v.count));
讓我們來梳理一下流程:
- 開始按鈕(1秒按鈕),1/2秒按鈕,1/4秒按鈕的點(diǎn)擊事件合并為一個(gè)事件流,事件流中的事件為三個(gè)值 1000,500,250(誰點(diǎn)擊就產(chǎn)生對(duì)應(yīng)的數(shù)字)。
- 時(shí)間數(shù)字來到了 switchMap 操作符,并作為輸入?yún)?shù)傳遞給了 switchMap 的函數(shù)參數(shù)。
- switchMap 的函數(shù)參數(shù)返回一個(gè)新的事件流,也就是我們之前的 addOneOrReset$。我們得把這個(gè)“積木”拆開得到 interval,并把時(shí)間數(shù)字傳遞給它。
最后我們可以把原來的 addOneOrReset$ 事件流改造一下:
const addOneOrReset = (time = 1000) =>
merge(
interval(time).pipe(
takeUntil(pauseBtnClick$),
mapTo(addOne)
),
resetBtnClick$.pipe(mapTo(reset))
);
把它改造為一個(gè)可以設(shè)置時(shí)間參數(shù)并帶有默認(rèn)值的積木。下面是最終實(shí)現(xiàn)完整代碼:
import React, { useRef, useEffect, useState } from "react";
import { fromEvent, interval, merge } from "rxjs";
import { takeUntil, switchMap, scan, startWith, mapTo } from "rxjs/operators";
export default function App() {
const [txt, setTxt] = useState("");
const pauseBtnRef = useRef(null);
const startBtnRef = useRef(null);
const resetBtnRef = useRef(null);
const halfBtnRef = useRef(null);
const quarterBtnRef = useRef(null);
const addOne = acc => ({ count: acc.count + 1 });
const reset = acc => ({ count: 0 });
useEffect(() => {
const pauseBtnClick$ = fromEvent(pauseBtnRef.current, "click");
const startBtnClick$ = fromEvent(startBtnRef.current, "click");
const resetBtnClick$ = fromEvent(resetBtnRef.current, "click");
const halfBtnClick$ = fromEvent(halfBtnRef.current, "click");
const quarterBtnClick$ = fromEvent(quarterBtnRef.current, "click");
const addOneOrReset = (time = 1000) =>
merge(
interval(time).pipe(
takeUntil(pauseBtnClick$),
mapTo(addOne)
),
resetBtnClick$.pipe(mapTo(reset))
);
const time$ = merge(
startBtnClick$.pipe(mapTo(1000)),
halfBtnClick$.pipe(mapTo(500)),
quarterBtnClick$.pipe(mapTo(250))
);
const subscription = time$
.pipe(
switchMap(addOneOrReset),
startWith({ count: 0 }),
scan((acc, current) => current(acc))
)
.subscribe(v => setTxt(v.count));
return () => {
subscription.unsubscribe();
};
}, []);
return (
<div className="App">
<div style={{ fontSize: "30px" }}>{txt}</div>
<button ref={startBtnRef}>開始</button>
<button ref={pauseBtnRef}>暫停</button>
<button ref={resetBtnRef}>重置</button>
<button ref={halfBtnRef}>1/2秒</button>
<button ref={quarterBtnRef}>1/4秒</button>
</div>
);
}
好了,如有任何問題,請(qǐng)?zhí)砑游⑿殴娞?hào)“讀一讀我”。