<img height="1" width="1" style="display:none" src="https://www.facebook.com/tr?id=1063935717132479&amp;ev=PageView&amp;noscript=1 https://www.facebook.com/tr?id=1063935717132479&amp;ev=PageView&amp;noscript=1 "> Bitovi Blog - UX and UI design, JavaScript and Front-end development

RxJS with React (Part 1)

Adam L Barrett

How to use RxJS Observables with your React components, because together they are incredible.

posted in Development, javascript, React on October 10, 2019 by Adam L Barrett


RxJS with React (Part 1)

Adam L Barrett by Adam L Barrett

RxJS and React go together like chocolate and peanut butter: great individually but they become something incredible when put together.

2 people colliding and accidentally mixing their chocolate bar and peanut butter, but then seemingly enjoying the mix

A quick search on npm will find a slew of hooks to connect RxJS Observables to React components, but let’s start at the beginning, because RxJS and React fit very well together “as is” because they follow the same philosophy and have very compatible APIs.

A quick aside about Why RxJS

2019 has been the year of RxJS, blowing up across the web-dev community with events like rxjs.live and ng-conf. More and more developers are finding out that RxJS is awesome and it is totally worth climbing your way through the somewhat steep learning curve.

Angular devs have been using RxJS for a while now. A quick search will find vue-rx, ember-rx, and even Svelte can use RxJS Observables as stores by default. When you learn RxJS you are learning a highly portable skill that can be used across frameworks. The concepts of Rx can actually be used across languages and platforms.

RxJS is a mature, battle hardened library for dealing with events and data flow. It is definitely going to be valuable to familiarize yourself with how it works.


Let’s start with a simple example:

We have a simple List component here that just lists the strings it is given:

const source = ['Adam', 'Brian', 'Christine'];

function App() {
  const [names, setNames] = useState(source);

  return (
    <div className="App">
      <h1>RxJS with React</h1>

      <List items={names} />
    </div>
  );
}

(follow along on CodeSandbox!)

Now, let’s pull those values from an RxJS Observable.

Let’s start by creating an Observable with the RxJS of() function.

We’ll need to:

  • add rxjs as a dependency (npm i rxjs, yarn add rxjs or however you need to if you're not using CodeSandbox)
  • import of from rxjs

Then let’s create an Observable called names$, whose value is the source array:

import { of } from 'rxjs';

const source = ['Adam', 'Brian', 'Christine'];
const names$ = of(source);

FYI: I will be following the convention of naming an Observable variable with a \$ suffix (aka Finnish Notation), which is completely optional but I think it may help for clarity while learning.

Now what we want to do is synchronize the component state with the state from the Observable. This would be considered a side-effect of the React function component App, so we are going to use the useEffect() hook, which we can import from react.

Inside the useEffect() callback we will:

  • subscribe to the names$ Observable with the subscribe() method, passing our "state setter function" setNames as the observer argument
  • capture the subscription returned from observable.subscribe()
  • return a clean-up function that calls the subscriptions .unsubscribe() method
function App() {
  const [names, setNames] = useState();

  useEffect(() => {
    const subscription = names$.subscribe(setNames);
    return () => subscription.unsubscribe();
  });

  return (
    <div className="App">
      <h1>RxJS with React</h1>
      <List items={names} />
    </div>
  );
}

Which at this point should look something like this:


The concepts and APIs in RxJS and React are very compatible: the way useEffect aligns with an RxJS subscription and how the clean-up call is a perfect time to unsubscribe. You’ll see a lot more of that "symbiosis" as we go on.

An aside about useEffect

When using useEffect to synchronize component state to some "outer" state, you must decide what state you want to sync with.

  • All state
  • No state
  • Some select pieces of state

This is represented in the deps array, which is the second argument passed to useEffect.

To use an quote from Ryan Florence:

useEffect(fn) // all state
useEffect(fn, []) // no state
useEffect(fn, [these, states])

So, in this instance we don’t have any props or other state to sync with: we just want our names array to be whatever is the current value of our Observable. We just want to update our component state whenever the Observables value changes, so we’ll go with No State and throw in an empty array [] as the second argument.

useEffect(() => {
  const subscription = names$.subscribe(setNames);
  return () => subscription.unsubscribe();
}, []);

Creating a custom hook

It looks like we’ll be using this pattern a lot:

  • subscribing to an Observable in useEffect
  • setting the state on any changes
  • unsubscribing in the clean-up function

…so let’s extract that behaviour into a custom hook called useObservable.

const useObservable = observable => {
  const [state, setState] = useState();

  useEffect(() => {
    const sub = observable.subscribe(setState);
    return () => sub.unsubscribe();
  }, [observable]);

  return state;
};

Our useObservable hook takes an Observable and returns the last emitted value of that Observable, while causing a re-render on changes by calling setState.

Note that our state is initialized as undefined until some value is emitted in the Observable. We’ll use that later, but for now, make sure the components can handle when the state is undefined.

So we should have something like this now:


Of course, we could, and probably should, have useObservable() defined as an export from a module in its own file because it is shareable across components and maybe even across apps. But for our simple example today, we’ll just keep everything in one file.

Adding some asynchronicity

So we have this list of names showing now, but this is all very boring so far, so let’s do something a little more Asynchronous.

Let’s import interval from rxjs and the map operator from rxjs/operators. Then, let’s use them to create on Observable that only adds a name to the list every second.

import { interval } from 'rxjs';
import { map } from 'rxjs/operators';

const source = ['Adam', 'Brian', 'Christine'];
const names$ = interval(1000).pipe(map(i => source.slice(0, i + 1)));

Neat. So we can see our list appearing one at a time. Sort of useless, but off to a good start. 😄

Fetching some data

Instead of our source array, let’s fetch the list of names from an API.

The API endpoint we’ll be using comes from randomuser.me, which is a nice service for just getting some made up user data.

We’ll add these 2 helper variables, api and getName which will allow us to fetch 5 users at a time and the function will help extract the name from the user data randomuser.me provides.

const api = `https://randomuser.me/api/?results=5&seed=rx-react&nat=us&inc=name&noinfo`;
const getName = user => `${user.name.first} ${user.name.last}`;

RxJS has some great utility functions for fetching data such as fromFetch and webSocket, but since we are just getting some JSON from an ajax request, we’ll be using the RxJS ajax.getJSON method from the rxjs/ajax module.

import { ajax } from 'rxjs/ajax';

const names$ = ajax
  .getJSON(api)
  .pipe(map(({ results: users }) => users.map(getName)));

This will fetch the first 5 users from the API and map over the array to extract the name from the name.first and name.last property on each user. Now our component is rendering the 5 names from the API, yay!


It’s interesting to note here, that since we moved our code into a custom hook, we haven’t changed the component code at all. When you decouple the data from the display of the component like this, you get certain advantages. For example, we could hook up our Observable to a websocket for live data updates, or even do polling in a web-worker, but the component doesn’t need to change, it is happy rendering whatever data it is given and the implementation of how the data is retrieved is isolated from the display on the page.

Aside about RxJS Ajax

One of the great benefits of using the RxJS ajax module (as well as fromFetch), is that request cancellation is built right in.

Because our useObservable hook unsubscribes from the Observable in the clean-up function, if our component was ever “unmounted” while an ajax request was in flight, the ajax request would be cancelled and the setState would never be called. It is a great memory safe feature built in without needing any extra effort. RxJS and React working great together, out of the box, again.

Reese's Peanut Butter Cups


Actions

So now we have this great custom hook for reading state values off an Observable. Those values can come from anywhere, asynchronously, into our component, and that is pretty good, but React is all about Data Down and Actions Up (DDAU). We’ve really only got the data half of that covered right now, what about the actions?

In the next instalment of the series we’ll explore Actions, how we model our RxJS integration after the built-in useReducer hook, and much much more.

If you have any questions, feel free to post in the comments, or you can join our Bitovi community Slack at https://bitovi.com/community/slack, and ask me directly. There are lots of other JavaScript experts there too, and it is a great place to ask questions or get some help.

Create better web applications. We’ll help. Let’s work together.