Queries for Observables: Crazy & Simple!

kosich

Kostia Palchyk

Posted on October 7, 2020

Queries for Observables: Crazy & Simple!

In the previous post we explored API to select events from streams using RegExp-like syntax, e.g.:

const D = fromEvent(element, 'mousedown');
const M = fromEvent(document, 'mousemove');
const U = fromEvent(document, 'mouseup');

exec(
  'DM*U'         // <- regular expression
  , { D, M, U }  // <- streams that will be used
)
  .pipe()
  .subscribe(console.log);
Enter fullscreen mode Exit fullscreen mode

In this post we'll review it's functional API counterpart:

query(D, some(M), U) // select some Ms between Ds and Us
  .pipe()
  .subscribe(console.log)
Enter fullscreen mode Exit fullscreen mode

But first, we'll recap the thought process. If you're already familiar with the idea — hop here

tl;dr: package @ https://github.com/erql/rx-rql 📦

💡 Recap the idea

Imagine we need to implement a drag-n-drop behavior.

For that, we have three event streams: mousemove$, mousedown$, mouseup$. So we want to capture mousemove$ events after mousedown$ emitted and before mouseup$.

Let's draw a marble diagram of the event streams we have:

mousedown$  --o------------
mousemove$  -o-o-o-o-o-o-o-
mouseup$    ------------o--
Enter fullscreen mode Exit fullscreen mode
Every event stream is represented with separate line.
o stands for event on the stream. - is a separator to represent passing time

For better readability let's substitute all the os to respective letters of the streams:

mousedown$  --d------------
mousemove$  -m-m-m-m-m-m-m-
mouseup$    ------------u--
Enter fullscreen mode Exit fullscreen mode
'd' will now stand for mouse-down event, 'm' for mouse-move, 'u' for mouse-up

Now that we have distinct event names, we can simplify our diagram to a single line of events:

events$     -mdm-m-m-m-mum-
Enter fullscreen mode Exit fullscreen mode

Let's remove the time - signs as well, we don't them:

events$      mdmmmmmum
Enter fullscreen mode Exit fullscreen mode

Okay, to rephrase our task in terms of the new diagram: we need to capture the m events between d and u emissions.

🤔

Hmm...

"we need ms between d and u"...

Sounds familiar...

Ah! If that was a string, we could easily do it with a regular expression:

/dm*u/.exec('mdmmmum')
Enter fullscreen mode Exit fullscreen mode

Would give us the needed dmmmu without trailing mouse-move m events...

Right?

If only we had a library to select events from streams with regexes...

🚀 Solution

query(D, some(M), U)
  .pipe()
  .subscribe(console.log)
Enter fullscreen mode Exit fullscreen mode

Rx-RQL 📦 package provides following API to make such selections:

  • query(…) — root of your selection
  • A — select 1 emission from the stream
  • some(A) — select 0 to ∞ emissions from A
  • maybe(A) — select 0 or 1 emission from A
  • many(n,m)(A) — select from n to m emissions from A
  • mute(A) — select emission from A & mute it

And you can group them as you like:

  • some(A, some(B), mute(C)) — select as many emissions from: select as many Bs as possible between emissions from A and muted C

Here's how to create a simple drag-n-drop behavior using this package:

And here's a Mr. Potato-Head DnD 🥔 — a more sophisticated example based on this amazing article by @dailydevtips1 ! Thx, Chris 🙏

👋 Outro

Thank you for reading this article! Stay reactive and have a nice day 🙂

If you enjoyed reading — please, indicate that with ❤️ 🦄 📘 buttons

And in case you're not yet following me here and on twitter — then you've probably missed my recent experiments: Rx + Proxy, Rx Autorun, React + Rx

Now I'd love to hear your thoughts! 👂

💖 💪 🙅 🚩
kosich
Kostia Palchyk

Posted on October 7, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related