RxJS pipe as a React hook
Tags: react, rxjs, javascript, typescript
Published: 2021-04-26 17:34:36

Ever tried to use an Rx Observable in React? Then you know what's the problem with this code:

```ts
function App() {
  let [time, setTime] = useState(0);
  timer(0, 1000)
    .pipe(
      filter(x => x % 2),
      map(x => x + '!')
    )
    .subscribe(setTime);

  return <h1>{ time }</h1>
}
```

Yeah, it subscribes to the `timer` with each render. Which triggers `setTime` on each `timer` emission. Which leads to a re-render. Which leads to... well, you know, memory leaks and weird behaviour. And on top of that it even won't be destroyed with the component unmount.

_In this short post I want to share with you a non-canonical idea (probably not original one) how to fix that!_

**tl;dr:** [online playground with hook pipe](https://stackblitz.com/edit/react-rx-mystery-timer?file=App.tsx)

![the hook](https://media.giphy.com/media/3oriO6BkBa8XhQLqWA/giphy.gif)

## 🪝 The hook

We could devise a custom react hook, that will fix that. Lets use a `useEffect` hook, which will subscribe to the source, and push messages to our observer (`setTime` in the example above)

```ts
let useObservable = (observable, observer) => {
  // useEffect with empty deps will call this only once
  useEffect(() => {
    let sub = observable.subscribe(observer); // connect
    return () => sub.unsubscribe(); // < unsub on unmount
  }, []);
}
```

And it will be used like this:

```ts
function App() {
  let [time, setTime] = useState(0);
  useObservable(
    timer(0, 1000)
      .pipe(
        filter(x => x % 2),
        map(x => x + '!')
      ),
    setTime
  );

  return <h1>{ time }</h1>
}
```

Which looks react-ish... but not rx-ish.
Not nice 😕. We can do better!

_So let's explore another way!_

## 🗞️ RxJS pipes

But before we continue, a quick reminder of RxJS pipe operator mechanics:

Roughly speaking RxJS pipe operator (like, `map`) is just a function that takes one Observable and returns a new Observable: 

```ts
(source: Observable<A>) => Observable<B>
```

So when we subscribe to the resulting `Observable<B>`, operator subscribes to the source `Observable<A>`. And when that source emits a value, operator applies its logic to it (`map`, `filter`, etc) and decides what, when, and how to push to the resulting `Observable<B>`. `map` will push modified values, `filter` will push only values that satisfy given condition.

_Okay, back to hooks_

## 🪝🗞️ The hook pipe

We can modify the hook to implement the Rx Operator interface, while still enclosing a `useEffect` hook.

Let's start with how we'll use it in a component:

```ts
function App() {
  let [time, setTime] = useState(0);

  timer(0, 1000)
    .pipe(
      filter(x => x % 2),
      map(x => x + '!'),
      useUntilUnmount()
    )
    .subscribe(setTime);

  return <h1>{ time }</h1>
}
```

And here's it's implementation:

```ts
function useUntilUnmount() {
  // Observable => Observable interface
  return source => new Observable(observer => {
    // create a new Subscription
    // we'll use it to handle un-mounts and unsubscriptions
    let sub = new Subscription();

    // this is run only once
    useEffect(() => {
      // connect observer to source
      sub.add(source.subscribe(observer));
      // on unmount -- destroy this subscription
      return () => sub.unsubscribe();
    }, []);

    // return sub to handle un-subscriptions
    return sub;
  });
}
```

This is really just 8 lines of code.

**Disclaimer**: while being leak-free and working as promised, this might not be the best way to use Observables in React. Tried [<$> fragment](https://dev.to/rxjs/fetching-data-in-react-with-rxjs-and-lt-gt-fragment-54h7) already?

## 🛸💨 Outro

Try our shiny hook pipe (with dependencies!) in this [online playground](https://stackblitz.com/edit/react-rx-mystery-timer?file=App.tsx) and leave a comment here with your opinion!

And in the future, when [pipeline operator `|>`](https://github.com/tc39/proposal-pipeline-operator) lands in JS, we'll probably substitute the `subscribe` with our custom hook subscribe. Like this:

```ts
function App() {
  let [time, setTime] = useState(0);

  timer(0, 1000)
    |> filter(x => x % 2)
    |> map(x => x + '!')
    |> subscribeHook(setTime)

  return <h1>{ time }</h1>
}
```

---

That's it for today! Follow me here and on [twitter](https://twitter.com/kddsky) for more RxJS, React, and JS posts!

I hope you had fun! If you enjoyed reading — please, indicate that with ❤️ 🦄 📘 buttons — it helps a lot!

Thank you for reading this article! Stay reactive and have a nice day 🙂

Cya! 👋

## Psst.. Check out my other Rx / React articles!

- ["Fetching Data in React with RxJS and <$> fragment"](https://dev.to/rxjs/fetching-data-in-react-with-rxjs-and-lt-gt-fragment-54h7)

- ["Turn a Stream of Objects into an Object of Streams"](https://dev.to/rxjs/turn-a-stream-of-objects-into-an-object-of-streams-2aed)

- ["Queries for Observables: Crazy & Simple!"](https://dev.to/kosich/queries-for-observables-crazy-simple-15h3)

- ["Intro to Recks: Rx+JSX experiment"](https://dev.to/kosich/recks-rxjs-based-framework-23h5)

😉

_header image by <a href="https://unsplash.com/@victor_g?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Victor Garcia</a> on <a href="https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>, gif taken from [giphy.com](https://giphy.com/gifs/cat-box-hook-3oriO6BkBa8XhQLqWA)_