Reactive Programming in React using RxJS Observables

An analysis of the same React App with hooks refactored using RxJS's Observables and operators.

published 3 months ago

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.

JavaDesignPatterns_Fig2.jpg

To be more precise, it fixes the mistake the authors of Design Patterns: Elements Of Reusable Object Oriented Software did years ago by finally connecting the Observer pattern and the Iterator pattern.

In an imperative programming paradigm a = b + c would mean that once this expression is executed, if the values of b or c changes, it wouldn't affect the value of a. But in Reactive Programming, the value of a changes.

a would go on to become an Observer. b and c would go on to become a part of a data stream being pushed into an Observable.

Rx is lodash for events

The implementation of Rx is right now spearheaded by ReactiveX. Anyone can head over, choose their platform and get a library which provides them with Rx operators in their own language. Operators are the heart of these libraries which help transform the data as it flows in the stream.

React and Hooks

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!

Loading...

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

Loading...

I am going to write a short post on async/await and how it's similar to promises. If they're still new to you, ping me on twitter and I will personally DM you when it's out.

Loading...

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 reads 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 tits up because it became ELI5 level easy to write worse code). But React is just a library. It is super amazing at what it does.

But maybe, just 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 me 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.

rx.gif

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.

Loading...

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.

Loading...

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.

Loading...

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.

Loading...

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 useObservable and 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 codesanbox for the prototypal app we just made:

Did you like this? Tell me!

Cheers!