RxJS Lap Button (From Koutnik’s RxJS book)

Categories: Coding, RxJS

In the first chapter of Randall Koutnik’s book Building Reactive Websites with RxJS he has a challenge where he asks the reader to add a lap button to the stopwatch project. This lap button essentially freezes the output while continuing to count behind the scenes. I couldn’t figure this out for a little while, and found no online resources to help me out. After some head banging I was able to figure out a solution:

[typescript]
// Imports
import {interval, fromEvent} from 'rxjs';
import {combineLatest, filter, map, scan, startWith, takeUntil} from 'rxjs/operators';

// HTML Elements
let start = document.querySelector('#start-button');
let stop = document.querySelector('#stop-button');
let lap = document.querySelector('#lap-button');
let output = document.querySelector<HTMLElement>('.output');

// Observables
let tenthSecond$ = interval(100);
let startClick$ = fromEvent(start, 'click');
let stopClick$ = fromEvent(stop, 'click');

// Lap logic
const lapClick$ = fromEvent(lap, 'click')
      .pipe(
        startWith(false),
        scan((acc, _) => acc = !acc),
      );

startClick$.subscribe(() => {
  tenthSecond$
    .pipe(
      combineLatest(lapClick$),
      filter(([ms, skip]) => !skip),
      map(([ms, _]) => ms / 10),
      takeUntil(stopClick$)
    )
    .subscribe(ms => output.innerText = ms + 's');
});
[/typescript]

I thought this was a pretty neat solution! Here’s the breakdown. lapClick$ is an Observable<boolean> which starts with false. Whenever it receives an emission, the scan will invert its current condition. That is, when the lap button is clicked once it becomes true then false etc.

Next, we use combineLatest to actually see into lapClick$ in our main function. We filter based on if the lapClick$ is true or not, if it’s not we continue with our normal logic. That’s it! I thought this was a fun solution but it took some thinking to actually arrive at the answer.

«
»