DEV Community

Voltra
Voltra

Posted on

My first major contribution: More rxjs in vue-use

Let me tell you the story of my first (actually relevant) major contribution to an Open Source project: adding more rxjs in vue-use.

My approach to Computer Science

I chose Computer Science because I like writing code, and I like solving problems. My approach to open source and Computer Science as a whole is the following:

Solve a problem, don't create a problem just to solve it

My problem

I was rewriting my music player and had to migrate the existing code from Vue 2 to Vue 3. I also had to, you know, actually finish the damn thing this time.

One thing I like to do when using Vue (or any other framework) is to completely decouple the business logic from the "application logic". I also like to completely decouple the "framework logic" from the business logic.

As such, I had written an AudioPlayer class. Trying to integrate the pieces together, I noticed something: I need an HTMLAudioElement to construct my AudioPlayer, but I can only ever get Ref<HTMLAudioElement|null> if I don't want to use directives.

That problem was easy enough to solve: make a computed property and wrap it nicely into a ReadonlyRef<AudioPlayer|null>.

Then the real issue came up: useObservable only accepts Observable<T> as input. I have a ReadonlyRef<AudioPlayer|null> which can give me a ReadonlyRef<Observable<T>>. I can't make that work with the existing composition functions, but I desperately want to!

So what did I do? I rolled my own!

What's new?

Introducing useExtractedObservable and watchExtractedObservable.

What do they do?

Precisely solve one problem (in two similar yet distinct ways): The lack of interoperability between Vue's composables and RxJS's observables.

How it went

I had wrote a first draft of them for myself, and thought:

If I have that problem, others will.

And thus I forked vue-use, looked at the implementation of useObservable and watch, and adapted them to make the new composables.

useExtractedObservable

Is a fancier version of useObservable that, as the name suggests, extracts the observable from something else (aka watch sources).

You map your extracted data into an observable, and turn that observable as a ref (well, technically really a shallowRef for performance reasons).

import { ref } from 'vue'
import { useExtractedObservable } from '@vueuse/rxjs'
import { interval } from 'rxjs'
import { mapTo, scan, startWith, takeWhile } from 'rxjs/operators'
// setup()
const start = ref<number>()
const count = useExtractedObservable(
  start,
  (start) => {
    return interval(1000).pipe(
      mapTo(1),
      startWith(start),
      scan((total, next) => next + total),
      takeWhile((num) => num < 10),
    )
  },
  {},
  {
    immediate: false,
  },
)
Enter fullscreen mode Exit fullscreen mode

watchExtractedObservable

Is the love child of watch and useExtractedObservable. Instead of getting the new values in a ref, you instead get to react to them via a callback.

import { computed, ref } from 'vue'
import { watchExtractedObservable } from '@vueuse/rxjs'
import { AudioPlayer } from '../my/libs/AudioPlayer'
// setup()
const audio = ref<HTMLAudioElement>()
const player = computed(() => (audio.value ? new AudioPlayer(audio) : null))
const state = reactive({
  progress: 0,
})
watchExtractedObservable(
  player,
  (p) => p.progress$,
  (percentage) => {
    state.progress = percentage * 100
  },
)
Enter fullscreen mode Exit fullscreen mode

Top comments (0)