React Hooks + RxJS Subjects

React Hooks + RxJS Subjects
React Hooks + RxJS or How React is meant to be used all along

I remember when I first got introduced to React. I was stunned by how great the idea of handling all of the presentation logic with just Javascript and JSX was, and even more so: how effortless it seemed at first–you pass in props, update states. How easy!

Going through the first Tic-Tac-Toe tutorial on the React homepage felt so great, I was confident that humanity has finally found the magic bullet, at least for the front-end.

But… there was a caveat. What if we lifted the state five floors up, but some of that state and state update logic was still needed on the first floor?

How about we pass some state one level down, then some of that state one level further and so on till it reaches the component? That’s a bit laborious but ok.

Oh, I forget, I need to change the shape of the state on the fifth floor… and props on the fourth… and props on the third… second… first…

Ouch! Is it by mistake or design? Turns out this state-to-props issue is by design, and this is where Redux steals the show. However, five minutes into the Redux tutorial you begin to realize that your excitement about React was an overreaction… or was it?

There were many attempts I know of to steal the show back for React, to name a few: React Context API as well as various state managers; but for me, none of them got the excitement I initially had for React back… except when I learned about RxJS.

Analysis

This article’s main goal is to justify that the RxJS and React combo allows for better readability, less boilerplate and being closer to React while allowing the same functionality as the popular state management frameworks and not being a framework itself. Let’s look at this approach more closely.

Better readability & less boilerplate

We’ve all had these moments

Readability is one of the first things I pay attention to as I choose a tool I am going to be working with. For me, the main criterion upon which I evaluate the quality of my own code is how well am I able to understand it a week after and a month after it was written.

Of course, the tool you are using isn’t always as impacting on your code readability and self-documentability as much as your own set of habits and choices, but it still may be a contributing factor nonetheless. In my experience, the more verbosity and indirection is associated with creating a specific functionality, the less you will be able to understand it later on.

And this is true of Redux. Before even getting to the core of what it is you are doing, you have to write your actions (some people write action creators on top of that), create reducers, compose them, connect dispatch and state to props, and only then introduce the logic, and don’t forget you have to write sagas or thunks for side effects.

When you are trying to understand the structure of a Redux application, you are facing similar challenges, you have to understand what actions trigger what side effects, what happens in reducers and how it all fits together. I’ve always considered it being a nightmare, and, despite my many attempts, never really understood my colleagues who were very passionate about Redux. I would say Redux and readability are essentially the opposites. Feel free to disagree, but please make an educated argument in the comments for me and other people to be able to learn.

MobX significantly improves the readability aspect by not having the verbosity and indirection of Redux. Yet it can be confusing to mentally derive the structure of the state that consists of composed stores, as they can become a mess of properties and update logic. So how does it compare to the RxJS Hook?

Since you’re essentially using RxJS Hook as the familiar useState hook, it should be very easy to read and understand. Your state lives inside of a Subject that you can always retrieve later and analyze as you are trying to understand what the application is doing.

Close to React

It’s quite an atomic family

React is elegant. The state managers are not. Period. If there is an opportunity to stay with React before introducing other frameworks, I will use that opportunity. I am convinced that you should be able to compose complex and elegant application code with essentially React and Services logic while not sacrificing readability, maintainability, and scalability.

RxJS and React Hooks are actually the combination I find extremely helpful in this regard. Having actually written production code with this, I can tell you from my experience that it was and still is a great ride.

State Management Functionality

Creation of the modern front-end universe

I never really understood the popularity of Redux. Don’t get me wrong, I always thought it was pretty interesting, but I never liked writing my code using it. I wonder if its popularity really comes from the fact that people like Dan Abramov, who is a great guy and a great engineer, but it doesn’t make sense why it would still worth biting the bullet.

The main merit of Redux for me is in its three principles of course, which are:

So none of these principles actually work for MobX! With MobX you don’t have a centralized state object. You have a centralized store object. Know the difference.

And since you don’t have a centralized state object, you can’t use it to easily reason about your application as your stores and your state are interleaved and can be pretty hard to unravel.

With RxJS Subjects and React Hooks we get the one principle out of the box – the pure-functions, as observable streams are themselves pure-functions. Unfortunately, the state of a BehaviorSubject is not read-only, and you can manually change it after you get it with getValue(). I will try to ask the community if it is possible to make it read-only out of the box so that you can’t shoot yourself in the foot with this, as I am pretty sure it could just be something the contributors have overlooked. But as to the single source of truth, it is actually not that difficult to introduce by composing all the subjects into one observable, similar to how you compose reducers together.

Single Source of Truth

const subject001 = new BehaviorSubject({});
const subject002 = new BehaviorSubject({});
const subject003 = new BehaviorSubject({});


// Only for listening
const globalObservable = combineLatest(subject001, subject002, subject003);

In general, the RxJS Hook approach is not that different from Flux. You have:

TL;DR

So here is your shared state!

const subject = new BehaviorSubject({ message: '' });


const AwesomeComponent = () => {
 const [{ message }, setState] = useSharedState(subject); // Custom Hook
 return <div>{message}</div>;
};

Nothing unusual: we are getting a tuple of value and setState function.

Do you want to use it like useState?

Easy!

const AwesomeComponent = () => {
 const [{ message }, setState] = useSharedState(subject);
 return <div
   onClick={() => setState({ message: 'test' })} // One way
   onClick={() => subject.next({ message: 'test' })} // Another way
 >{message}</div>;
};

subject.next is another way to update the logic, but I think setState from the tuple makes it more readable and easy to understand.

Do you want it to be shared between components at the top of the tree as well as the bottom?

Easy!

const First = () => {
 const [{ message }, setState] = useSharedState(subject);
 return <div>First: {message}</div>
}
const Second = () => <div><First/></div>
const Third = () => <div><Second/></div>
const Fourth = () => <div><Third/></div>


const Fifth = () => {
 const [{ message }, setState] = useSharedState(subject);
 return <div><div>Fifth: {message}<div><Fourth/></div>
}

Notice that we can use the same state easily, where in the past you would have to pass everything through props.

Do you want to use both local and shared states?

Easy!

// Single shared and local states
const AwesomeComponent = () => {
 const [sharedState, setSharedState] = useSharedState(subject);
 const [state, setState] = useState(‘’);
 return <div>{/* That was easy */}</div>;
};


// Multiple shared and local states
const AwesomeComponent = () => {
 const [sharedState001, setSharedState001] = useSharedState(subject001);
 const [sharedState002, setSharedState002] = useSharedState(subject002);
 const [sharedState003, setSharedState003] = useSharedState(subject003);
 const [state001, setState001] = useState('test');
 const [state002, setState002] = useState('test');
 const [state003, setState003] = useState('test');
 return <div>{/* That was easy */}</div>;
};

Another great thing is that you actually can have multiple little states as part of your component state.

Oh, but what if I want to update this state outside the component…

Easy!

subject.next({ message: 'I know Kung Fu' });

Or so the course certificate says

You can update state from anywhere in the application, by just using subject.next. It is extremely useful! MobX and Redux could never give you this much freedom.

Still not convinced?

I rewrote the Redux tutorial using the RxJS Hooks approach. Feel free to compare two codebases that do the same thing.

Side Notes

I think somebody brought to my attention that using components with subjects makes them tightly coupled. It is true in a sense where, for example, you are importing something from an external library which makes a hard dependency. But having hard dependencies doesn’t mean you can’t make whatever it is you are building highly reusable. A styled component inside of my custom component is a hard dependency, but it is abstracted out when I import my custom component. The same goes for this approach.

Conclusion

Behind the custom hook is a little pretty eight-liner:

const useSharedState = subject => {
 const [value, setState] = useState(subject.getValue());
 useEffect(() => {
   const sub = subject.pipe(skip(1)).subscribe(s => setState(s));
   return () => sub.unsubscribe();
 });
 const newSetState = state => subject.next(state);
 return [value, newSetState];
};

Just an ordinary day of a React developer

Thanks for reading

If you liked this post, share it with all of your programming buddies!

Follow us on Facebook | Twitter

Further reading about React

React - The Complete Guide (incl Hooks, React Router, Redux)

Modern React with Redux [2019 Update]

Best 50 React Interview Questions for Frontend Developers in 2019

JavaScript Basics Before You Learn React

Microfrontends — Connecting JavaScript frameworks together (React, Angular, Vue etc)

Reactjs vs. Angularjs — Which Is Best For Web Development

React + TypeScript : Why and How

How To Write Better Code in React

React Router: Add the Power of Navigation

Getting started with React Router

Using React Router for optimizing React apps

Creating RESTful APIs with NodeJS and MongoDB Tutorial

Building REST API with Nodejs / MongoDB /Passport /JWT