(guest@joequery.me)~ $ |

WTF is RxJS? Commentary part 1

NOTE: This article is a note-taking interactive Desktop experience, intended primarily for my own learning. It is not intended to be authoritative, but it may still be helpful on your journey.

Hi!

So I've heard the term "Observable" before, but I don't know what it means. Let's change that!

A search engine query of "introduction to rxjs" leads us here RxJS Primer

So WTF is RxJS and all this Observable stuff

From the site

An observable represents a stream, or source of data that can arrive over time. You can create an observable from nearly anything, but the most common use case in RxJS is from events. This can be anything from mouse moves, button clicks, input into a text field, or even route changes. The easiest way to create an observable is through the built in creation functions. For example, we can use the fromEvent helper function to create an observable of mouse click events:

Alrighty so RxJS seems to be a way of handling events. Why do events need to be handled in such a way? What does RxJS offer exactly? Let's read further and see.

Hello world example

This upcoming example was taken from the site docs with some slight modifications. Note that this may not be the proper way of doing things. I'm still learning :)

The following example:

  • Sets up an event listener on our button for click events.
  • Calls the handleClick function on each click event
  • Unsubscribes the click observer after MAX_CLICKS number of clicks

Again - this is most likely not at all the proper way to do this. This is just part of my learning process. Do not use this in a real project or you might be sad



const { fromEvent } = rxjs;
const MAX_CLICKS = 4                  // Edit this for fun!
const WELCOME_MESSAGE = 'Click me!'   // Edit this for fun!
const DONE_MESSAGE = 'Done!'          // Edit this for fun!

let numClicks, clickSubscription
const $button = document.getElementById('hello-world-button');
const $reset = document.getElementById('hello-world-reset');
const $updatedAt = document.getElementById('hello-world-updated');

const handleClick = (event) => {
    $button.innerText = 'Clicks remaining: ' + (MAX_CLICKS - ++numClicks)
    if(numClicks === MAX_CLICKS){
        clickSubscription.unsubscribe()
        $button.innerText = DONE_MESSAGE
    }
}

const reset = (event) => {
    numClicks = 0
    const date = new Date()
    $updatedAt.innerText = 'Updated at ' + date.toLocaleTimeString()
    $button.innerText = WELCOME_MESSAGE
    const clickObservable = fromEvent($button, 'click');
    if(clickSubscription) clickSubscription.unsubscribe()
    clickSubscription = clickObservable.subscribe(handleClick)
}

const resetObs = fromEvent($reset, 'click');
resetObs.subscribe(reset)
reset()

Quiz 1 - Handling trivial input changes

Oh you thought there wouldn't be quizzes?

Given the above example, can you finish the code below so that typing into the input box displays the uppercased version of the text entered?

(Again: This may not be the idiomatic way to do this...but working with bad code is something you have to do on the job anyway :P )



/**
 * NOTES:
 * The 'input' event fires when an <input> element is changed
 * Use $domNode.innerHTML = 'the text' to set the text within a DOM node
 */
const { fromEvent } = rxjs;
const $input = document.getElementById('quiz-1-input')
const $output = document.getElementById('quiz-1-output')

const handleInput = (event) => {
    const text = event.target.value.toLocaleUpperCase();
    // Do something here
}

// Do something here
''

Click to reveal solution

Solution!



const { fromEvent } = rxjs;

const $input = document.getElementById('quiz-1-solution-input')
const $output = document.getElementById('quiz-1-solution-output')

const handleInput = (event) => {
    const text = event.target.value.toLocaleUpperCase()
    $output.innerHTML = event.target.value.toLocaleUpperCase()
}

const obs = fromEvent($input, 'input')
obs.subscribe(handleInput)
''

Operators and stuff

So an observable is like a stream of data. RxJS comes with operators for acting on these streams.

Here is an example taken from this tutorial:



// Example from https://codesandbox.io/embed/rxjs-react-alarm-clock-ccvru
const { of, interval, concat, Subject } = rxjs
const {
  takeWhile,
  takeUntil,
  scan,
  startWith,
  repeatWhen,
  share,
  filter,
} = rxjs.operators;

const countdown$ = interval(1000)
  .pipe(
    startWith(5),
    scan(time => time - 1),
    takeWhile(time => time > 0)
  )
  .pipe(share());

const actions$ = new Subject();
const snooze$ = actions$.pipe(filter(action => action === 'snooze'));
const dismiss$ = actions$.pipe(filter(action => action === 'dismiss'));

const snoozeableAlarm$ = concat(countdown$, of('Wake up! 🎉')).pipe(
  repeatWhen(() => snooze$)
);

const observable$ = concat(
  snoozeableAlarm$.pipe(takeUntil(dismiss$)),
  of('Have a nice day! 🤗')
);

function OperatorIntro() {
  const [state, setState] = React.useState();
  React.useEffect(() => {
    const sub = observable$.subscribe(setState);
    return () => sub.unsubscribe();
  }, []);

  return (
    <React.Fragment>
      <h3>Alarm Clock</h3>
      <div className="display">{state}</div>
      <button className="snooze" onClick={() => actions$.next('snooze')}>
        Snooze
      </button>
      <button className="dismiss" onClick={() => actions$.next('dismiss')}>
        Dismiss
      </button>
    </React.Fragment>
  );
}

ReactDOM.render(React.createElement(OperatorIntro, null), container);

We'll look at operators in a separate article

Tagged as javascript, rxjs

Date published - April 25, 2021