RxJS with React
Tags: rxjs, react, hooks
Published: 2019-10-10 23:49:40

---
title: RxJS with React
published: true
description: How to use RxJS Observables with your React components, because together they are incredible
cover_image: https://thepracticaldev.s3.amazonaws.com/i/r7x06d1azvhoxkcldv4g.jpg
canonical_url: https://www.bitovi.com/blog/rxjs-with-react-pt-1
tags: RxJS, React, hooks
series: RxJS with React
---

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](https://thepracticaldev.s3.amazonaws.com/i/wjfugclfqoikgb64ezq9.gif)

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](https://www.rxjs.live/) and [ng-conf](https://www.ng-conf.org/). 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’ve got a simple `List` component here that just lists the strings it is given:

```jsx
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>
  );
}
```

{% codesandbox 6f7bw %}

_(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()`](https://rxjs.dev/api/index/function/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:

```jsx
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](https://medium.com/@benlesh/observables-and-finnish-notation-df8356ed1c9b)), 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()`](https://reactjs.org/docs/hooks-effect.html) 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

```jsx
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:

{% codesandbox ox99d %}

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:

{% twitter 1125041041063665666 %}

> `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.

```jsx
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`.

```jsx
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:

{% codesandbox 3qpvw %}

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’ve got 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.

```jsx
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)));
```

{% codesandbox 7xxyc runonclick=1 %}

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

### 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](https://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.

```js
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](https://rxjs.dev/) 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.

```js
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!

{% codesandbox 1gcbf %}

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](https://rxjs.dev/api/ajax/ajax) (as well as [fromFetch](https://rxjs.dev/api/fetch/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](https://thepracticaldev.s3.amazonaws.com/i/p66hp5q76bckyjj9velo.jpg)

---

### 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.