RxJS with React: Actions and Subjects
Tags: rxjs, react, hooks, observables
Published: 2020-08-31 04:13:15

---
title: RxJS with React: Actions and Subjects
published: true
description: How to use RxJS Observables with your React components, because together they are incredible
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/2gadh2nwgwp14twqxygh.jpg
tags: RxJS, React, hooks, observables
series: RxJS with React
---

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

![Chocolate and Peanut Butter Accidents](https://media.giphy.com/media/MAuH5cM8TOkt5dt6w6/giphy.gif)

### Actions

So in [the last article](https://dev.to/bitovi/rxjs-with-react-jek), we looked at how you can use the React built-in hooks `useState` and `useEffect` to subscribe to RxJS Observables. We created a custom `useObservable` hook that we can pass an RxJS Observable to, which will return the current value of the Observable and re-render every time the value changes.

In the React community, there used to be a lot more talk about the concept of Data Down and Actions Up (DDAU). With our `useObservable` custom hook, we've got the "data down" part cased, so now we need to handle the **actions**.

What are **actions** anyway?

Nowadays, there are 2 meanings to what we call React **actions**:

1. The original meaning, which was just about passing functions (callbacks) as props to communicate with your parent component. There is no 2-way binding in React, so if the child component wants to tell its parent component someone clicked a button or changed an input, it calls a function that was passed to it as a prop, which is sometimes known as the delegate pattern. **Actions** are just the act of calling those delegate prop functions (callbacks), like `onChange` or `onPause`.

2. Redux popularized a new meaning for **actions** though. In Redux, actions are serializable objects that describe the interaction or intent of the user. Conventionally they have a `type` property, and optional `payload` and `meta` properties as well. The **action** objects are _**dispatched**_ into the Redux store by calling the store's `dispatch()` method, and passing the **action** object as an argument.

But whether **actions** are the act of calling a delegate callback function, or an object describing the intent passed to a `dispatch` call, it still represents the idea that "something happened", and I would like to _send a message_ to my parent component, the store, or whatever, describing what happened.

### Subjects

RxJS [Subjects](https://rxjs.dev/api/index/class/Subject) are a great fit for dispatching actions. `Subjects` are special Observables that can also act as **observers**, because they implement the `observer` interface, which is just a fancy way of saying it has the methods `next()`, `error()`, and `complete()`. You can dispatch **actions** by calling a subject's `.next()` method, and passing whatever description of the event you need as an argument: Whatever argument we pass to `.next()` is emitted to all the subject's **observers**, sometimes called subscribers.

We can use RxJS's `Subject` to implement something like a Redux store. We can derive our `State` observable, as a combination of the current state, and an **observable of actions** which we get from using our Action `Subject`.

To get a clearer picture of what that means, let's use the simple `useObservable` custom hook we wrote in [Part 1](https://dev.to/bitovi/rxjs-with-react-jek) to create a simple count widget.

We'll create an observable of state (count), out the observable of actions + the current state:

```js
// this will be an observable of `increment` or `decrement` strings
const action$ = new Subject();
// map the action strings to a state update number
const update$ = action$.pipe(
  map((action) => (action === "increment" ? +1 : -1))
);
// update the state by summing the state and the update
const count$ = update$.pipe(
  startWith(0), // our initial state will be 0
  scan((count, update) => count + update)
);
```

...and a widget component that uses the `count$` observable and the custom `useObservable` hook to get a `count` state, and also uses the `action$` Subject to update the state by passing `increment` or `decrement` actions to the `action$.next()` method.

```jsx
const CountWidget = () => {
  const count = useObservable(count$);
  return (
    <div className="count-widget">
      <button onClick={() => action$.next("decrement")}>-</button>
      <span>{count}</span>
      <button onClick={() => action$.next("increment")}>+</button>
    </div>
  );
};
```

Here is the simple demo of the above.

{% codesandbox uou9x %}

This is simplistic but the idea can be expanded to something more useful. If we combine the same technique with our User Name Fetcher from [Part 1](https://dev.to/bitovi/rxjs-with-react-jek), we could easily add a paging feature to give the user the ability to navigate through a list of items. We create a callback function that takes an Observable of the `'back'` and `'forward'` actions dispatched in our component, and based on that, it fetches new "pages" of users, by increasing or decreasing the `page` query parameter in the API call.

{% codesandbox fz3kp %}

Though the example is a little more involved, the idea is the same, create an observable of "page number" which is derived from the actions, and use the `page$` observable to derive the list of names from an API call.

### Something like `useReducer`

One of the nice aspects of the React built-in hook [`useReducer`](https://reactjs.org/docs/hooks-reference.html#usereducer) is that you can define the reducer outside of the component. You can test the reducer function independently, and you know when you pass it to `useReducer` React will just update the state and re-render the component automatically.

Let's change our `useObservable` hook to have the same qualities.

To achieve this, we will alter our `useObservable` hook to take a function instead. The function passed to `useObservable` will receive an **Observable of actions** (the actions we dispatch from the component) as an argument, and will be expected to return an Observable of our new state. We’ll model the API for our custom hook after`useReducer()`, so it will return a tuple of   
`[state, dispatch]`.  
This way, we can leave it up to the developer how they want to respond to the dispatched actions and how it will affect the state.

Something like this:

```js
useObservable((action$) => {
  // let the developer decide how the action$ Observable affects the state
  actions$.pipe(/* … */);
  // returns an observable that emits the new state
  return newState$;
});
```

So to implement our new `useObservable()` custom hook we will:

- take a callback function `fn` as an argument;
- create an RxJS `Subject` as our `action$` observable;
- create a `dispatch` function that passes it's argument to `action.next()`;
- create a `state$` Observable by calling the `fn` callback and passing the `action$` as an argument
- pull the `state` out of the `state$` observable using the same `useState`/`useEffect` technique as before
- return the new `state` and the `dispatch` function as a `[state, dispatch]` tuple

With that we end up with something like this:

```js
const useObservable = (callback) => {
  // create the action$ observable only 1 time
  const action$ = useRef(new Subject()).current;
  // the dipatch function is memoized with useCallback()
  const dispatch = useCallback((v) => action$.next(v), [action$]);
  // store the callback on a ref, ignoring any new callback values
  const fn = useRef(callback).current;

  const [state, setState] = useState();
  useEffect(() => {
    // use the callback to create the new state$ observable
    const state$ = fn(action$);

    const sub = state$.subscribe(setState);
    return () => sub.unsubscribe();
  }, [fn, action$]);

  return [state, dispatch];
};
```

This looks a little like `useReducer` now, except that while `useReducer` is limited to synchronous updates to state, our `useObservable` can update state over time. Also, our `useObservable` is a safe async-hook, because it unsubscribes on clean-up, so you don’t have to worry about updating a components state after it has been unmounted.

#### Updating the example

Now, with that in place, we can define a `getUserNames()` function that follows our expected `useObservable` interface. Our `getUserNames()` function can be separate, isolated from our component. We can test it independently and, in theory, use the same functionality in different components. We'll extract the name fetching functionality into its own file and export the function `getUserNames`.

```js
import { map, startWith, scan, switchMap } from "rxjs/operators";
import { ajax } from "rxjs/ajax";

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

export const getUserNames = (action$) => {
  const actionMap = {
    forward: +1,
    back: -1,
  };

  const page$ = action$.pipe(
    scan((page, action) => page + actionMap[action], 1),
    startWith(1)
  );

  return page$.pipe(
    switchMap((page) => ajax.getJSON(`${api}&page=${page}`)),
    map(({ results }) => results.map(getName))
  );
};
```

Then our component would import `getUserNames` and along with our new `useObservable` and look something like this:

```jsx
function App() {
  const [names, dispatch] = useObservable(getUserNames);

  return (
    <div className="App">
      <h1>RxJS with React</h1>
      <List items={names} />
      <button onClick={() => dispatch("back")}>⇦</button>
      <button onClick={() => dispatch("forward")}>⇨</button>
    </div>
  );
}
```

Here is the example in full:

{% codesandbox d0hjy %}

I think this is a really nice pattern: it is obvious what the component does, the presentation is decoupled from how the data is actually retrieved, it follows the flux pattern, and generally aligns nicely with React model of component state and side effects.

![Cheering](https://media.giphy.com/media/cOB8cDnKM6eyY/giphy.gif)

---

This is really just scratching the surface though, our `useObservable` hook could be improved in many ways, including exposing the current state to the callback function, using memoization and other techniques to improve the performance, and offering some way to allow component props/state to be exposed to the callback function as a stream of props.

If you'd like to see a more robust implementation of these ideas, you can check out my [use-epic](https://github.com/BigAB/use-epic) library on GitHub which follows a very similar pattern.

{% github BigAB/use-epic no-readme %}

There is so much more that could be done when mixing React with RxJS: Animations, real-time updates, entity stores... the list goes on and on. If you'd be interested in any of those topics, let me know in the comments.