Reactive Programming in React using RxJS Observables

October 12, 2020

React is my favourite library for building UIs.

It's concept of triggering a render any time the state changes or the props change encourage best coding practices. When I came to react, I saw how close it was trying to mimic purity of functions. Because a React component is nothing but a function which takes certain arguments as props and returns a virtual-ish DOM node.

For every time we provide the same set of arguments, a component shall spit out the same output. That's purity. And that's amazing because it makes code less error prone. And to top it all, it's close to declarative programming. The only significant area where it goes towards imperative code is hooks and it's rules. Not a fan of the rules but hey, it works.

However, the more I worked on building UIs, the more I could see how quickly a codebase can become imperative-ish. React is pretty low level when it comes to closeness with Javascript and I realized that Javascript, does not treat events the same way it treats collections. That's when I stumbled upon a different paradigm.

Reactive Programming

A few months ago, I stumbled upon Reactive Programming. A software design paradigm which changes the way we code on the grassroot level. It deals more with data streams and the how the the flow of control of a code reacts to it.

Let's make a quick prototype app in react which utilizes Teleport's APIs and does some ajaxy searching. Check out Teleport tho. Really cool!

lodash's debounce function actually has a cancel method which if returned, clears the debouncing. Check the docs or the source

I'm a fan of React, yes, absolutely. I use it at work a lot. But the whole flow of control seems disjointed to me. It disjointed. Here, have a look:

  • setSearch text in the state as the user types
  • Pass it on to a side effect which will spit out a debounced text
  • Then, use that to perform an API request.

I have been using React for a such a long time but I never really realized that this whole approach was somewhat, dare I say, hacky. Yes, in a class based App, you would have a method that can be onChange={e => debounce.call..} but it, in the end, is still hacky.

For sure, frameworks have a different ways to go about it. (AngularJS did it with their $scope service and adding watchers.. and more watchers with custom directives and 2 way binding. Then that all went out the door because it became very easy to write worse code). But React is just a library. It is super amazing at what it does.

But maybe, if the merit and the worth of the input element would be just raised to a first class citizen of the whole DOM, this hacking would cease to exist. No more imperative-ish code and less error prone. Kind of like in Javascript, functions are first class citizens.

Turns out, RxJS gives us that opportunity.

Observables

I was asking myself, can we raise the merit and worth of that innocent input element so that we can code how to react when to its behaviour? Until I read the docs and was enlightened. We will see that as soon as we understand clearly what Observables are:

An Observable is something which notifies it's observer that it's value has changed so that the observer can react to it if it wants to.

A plain observable is unicast - only one observer gets notified. A Subject is multicast - more than one observers get notified.

If our innocent input element is upgraded to an observable, we can observe it's changes, transform data, and pass on the data like a funnel. Let's get down to business. No hacks, I promise.

A BehaviorSubject is a special type of Subject which emits the last remembered value. If it's value is changing and another observer starts listening to it, it will get the last remembered value.

Think of BehaviorSubject as your slack channel and you, someone who opened the channel, are an observer.

Since the search field is now becoming an Observable, we don't really need to handle the debouncing in a side effect. RxJS gives debounce as one of the many operators that can be used on any of it's Observables.

The data will funnel from the Observable and to the observer (results$ in the soon upcoming diff). We will use RxJS's operators on every level of the funnel. One one of those levels will be our debounce operator. But first, a detour.

TC39 hasn't really embraced Observables yet so we will still resort to letting React know we need to use an Observable in the App.

We are taking away most of the side effects from the App since we defined the searchSubject outside the scope of App. Useless re-renders are now nipped in the bud. The only side effect necessary now is through the observable. Which is what we initially wanted.

Like I mentioned, RxJS provides operators through which you could pipe the data and transform it on the way through the funnel.

Now, every time the <input/> is typed in, that data, or 'stream' is pushed through to our BehaviorSubject searchSubject which is a multi-cast - meaning that it supports multiple 'listeners'.

One such listener is our results$ which we are using by telling our App that we need to useObservableand leverage Rx.

Every time, the input changes, the last remembered data is pumped into the observable. The data goes through several levels of operators in the funnel:

  • filter only that stream where the user has typed atleast 1 character
  • debounce it with a certain constant time
  • only emit this current value if it was changed from the last remembered value in the BehaviorSubject.
  • flatten the response that we got from triggering an observable made from a promise

And lastly, every time this happens, set the result$ as details in our state and trigger a re-render of the App.

Conclusion

RxJS is really powerful. This example is barely even scratching the surface. The moment we start imagining events like arrays, a whole different world of interesting views open up. For another example, you've probably made use of the drag and drop events.

Imagine if instead of an event spitting back (x,y) coordinates of the cursor, it would be a stream of coordinates. You can tap into it, you can funnel the stream as you receive it. You can have another observer observing this stream and duplicate the coordinates and just like that, you would have two streams instead of one. A great case if you want to have an interactive drawing board that supports live editing.

I'm not saying most of these stuff can't be done right now with the technologies at had. I'm advocating that RxJS makes building them much much easier, cleaner, and maintainable.

Here's the codesandbox for the prototypal app we just made: