Best Practices for Managing RxJS Subscriptions
Tags: javascript, rxjs, angular, codenewbie
Published: 2020-12-15 09:18:27

When we use RxJS, it's standard practice to subscribe to Observables. By doing so, we create a [`Subscription`](https://rxjs.dev/api/index/class/Subscription). This object provides us with some methods that will aid in managing these subscriptions. This is very important, and is something that should not be overlooked! 

## Why do we care about subscription management?

If we do not put some thought into how we manage and clean up the subscriptions we create, we can cause an array of problems in our applications. This is due to how the [Observer Pattern](https://en.wikipedia.org/wiki/Observer_pattern) is implemented. 

When an Observable emits a new value, its Observers execute code that was set up during the subscription. For example:

```js
obs$.subscribe(data => doSomethingWithDataReceived(data));
```

If we do not manage this subscription, every time `obs$` emits a new value `doSomethingWithDataReceived` will be called. 

Let's say this code is set up on the Home View of our App. It should only ever be run when the user is on the Home View. Without managing this subscription correctly when the user navigates to a new view in the App, `doSomethingWithDataReceived` could still be called, potentially causing unexpected results, errors or even hard-to-track bugs.

## So what do we mean by Subscription Management?

Essentially, subscription management revolves around knowing when to `complete` or `unsubscribe` from an Observable, to prevent incorrect code from being executed, especially when we would not expect it to be executed.

We can refer to this management of subscriptions as cleaning up active subscriptions.

## How can we clean up subscriptions?

So, now that we know that managing subscriptions are an essential part of working with RxJS, what methods are available for us to manage them? 

### Unsubscribing Manually

One method we can use is to unsubscribe manually from active subscriptions when we no longer require them. RxJS provides us with a convenient method to do this. It lives on the `Subscription` object and is called `.unsubscribe()`.

If we take the example we had above; we can see how easy it is to unsubscribe when we need to:

```js
let homeViewSubscription = null;

function onEnterView() {
  homeViewSubscription = obs$.subscribe(data => doSomethingWithDataReceived(data));
}

function onLeaveView() {
  homeViewSubscription.unsubscribe();
}
```

1. We create a variable to store the subscription.
2. We store the subscription in a variable when we enter the view.
3. We unsubscribe from the subscription when we leave the view preventing `doSomethingWithDataReceived()` from being executed when we don't need it.

This is great; however, when working with RxJS, you will likely have more than one subscription. Calling `unsubscribe` for each of them could get tedious. A solution I have seen many codebases employ is to store an array of active subscriptions, loop through this array, unsubscribing from each when required.

Let's modify the example above to see how we could do this:

```js
const homeViewSubscriptions = [];

function onEnterView() {
  homeViewSubscriptions.push(
      obs$.subscribe(data => doSomethingWithDataReceived(data)),
      anotherObs$.subscribe(user => updateUserData(user))
    );
}

function onLeaveView() {
  homeViewSubscriptions.forEach(subscription => subscription.unsubscribe());
}
```

1. We create an array to store the subscriptions.
2. We add each subscription to the array when we enter the view.
3. We loop through and unsubscribe from the subscriptions in the array.

These are both valid methods of managing subscriptions and can and should be employed when necessary. There are other options. However, that can add a bit more resilience to your management of subscriptions.

### Using Operators

RxJS provides us with some operators that will clean up the subscription automatically when a condition is met, meaning we do not need to worry about setting up a variable to track our subscriptions.

Let's take a look at some of these!

#### `first`

The [`first`](https://rxjs.dev/api/operators/first) operator will take only the first value emitted, or the first value that meets the specified criteria. Then it will complete, meaning we do not have to worry about manually unsubscribing. Let's see how we would use this with our example above:

```js
function onEnterView() {
    obs$.pipe(first())
        .subscribe(data => doSomethingWithDataReceived(data))
}
```

When `obs$` emits a value, `first()` will pass the value to `doSomethingWithDataReceived` and then unsubscribe!

#### `take`

The [`take`](https://rxjs.dev/api/operators/take) operator allows us to specify how many values we want to receive from the Observable before we unsubscribe. This means that when we receive the specified number of values, `take` will automatically unsubscribe!

```js
function onEnterView() {
    obs$.pipe(take(5))
        .subscribe(data => doSomethingWithDataReceived(data))
}
```

Once `obs$` has emitted five values, `take` will unsubscribe automatically!

#### `takeUntil`

The [`takeUntil`]() operator provides us with an option to continue to receive values from an Observable until a different, `notifier` Observable emits a new value. 

Let's see it in action:

```js

const notifier$ = new Subject();

function onEnterView() {
      obs$.pipe(takeUntil(notifier$)).subscribe(data => doSomethingWithDataReceived(data))
}

function onLeaveView() {
  notifier$.next();
  notifier$.complete();
}

```

1. We create a `notifier$` Observable using a Subject. _(You can learn more about [Creating Observables here](https://labs.thisdot.co/blog/creating-observables-in-rxjs).)_
2. We use `takeUntil` to state that we want to receive values until `notifier$` emits a value
3. We tell `notifier$` to emit a value and complete _(we need to clean `notifer$` up ourselves) when we leave the view, allowing our original subscription to be unsubscribed.

#### `takeWhile`

Another option is the `takeWhile` operator. It allows us to continue receiving values whilst a specified condition remains true. Once it becomes false, it will unsubscribe automatically.

```js
function onEnterView() {
      obs$
        .pipe(takeWhile(data => data.finished === false))
        .subscribe(data => doSomethingWithDataReceived(data))
}
```

In the example above we can see that whilst the property `finished` on the data emitted is `false` we will continue to receive values. When it turns to `true`, `takeWhile` will unsubscribe!

### BONUS: With Angular

RxJS and Angular go hand-in-hand, even if the Angular team has tried to make the framework as agnostic as possible. From this, we usually find ourselves having to manage subscriptions in some manner.

### `async` Pipe

Angular itself provides one option for us to manage subscriptions, the `async` pipe. This pipe will subscribe to an Observable in the template, and when the template is destroyed, it will unsubscribe from the Observable automatically. It's very simple to use:

```html
<div *ngIf="obs$ | async as data">
  {{ data | json }}
</div>
```

By using the `as data`, we set the value emitted from the Observable to a template variable called `data`, allowing us to use it elsewhere in the children nodes to the `div` node.

When the template is destroyed, Angular will handle the cleanup!

#### `untilDestroyed`

Another option comes from a third-party library developed by [Netanel Basal](https://twitter.com/NetanelBasal). It's called [`until-destroyed`](https://github.com/ngneat/until-destroy), and it provides us with multiple options for cleaning up subscriptions in Angular when Angular destroys a Component.

We can use it similarly to `takeUntil`:

```ts
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';

@UntilDestroy()
@Component({
    selector: 'home'
})
export class HomeComponent implements OnInit {
  ngOnInit() {
    obs$
      .pipe(untilDestroyed(this))
      .subscribe(data => this.doSoemthingWithDataReceived(data));
  }
}
```

It can _also_ find which properties in your component are `Subscription` objects and automatically unsubscribe from them:

```ts
@UntilDestroy({ checkProperties: true })
@Component({
    selector: 'home'
})
export class HomeComponent {

  subscription = obs$
      .pipe(untilDestroyed(this))
      .subscribe(data => this.doSoemthingWithDataReceived(data));
}
```

This little library can be beneficial for managing subscriptions for Angular!

## When should we employ one of these methods?

The simple answer to this question would be:

> When we no longer want to execute code when the Observable emits a new value

But that doesn't give an example use-case. 

- We have covered one example use case in this article: when you navigate away from a view in your SPA.  
- In Angular, you'd want to use it when you destroy Components.  
- Combined with State Management, you could use it only to select a slice of state once that you do not expect to change over the lifecycle of the application. 
- Generally, you'd want to do it when a condition is met. This condition could be anything from the first click a user makes to when a certain length of time has passed.

Next time you're working with RxJS and subscriptions, think about when you no longer want to receive values from an Observable, and ensure you have code that will allow this to happen!

---
*This Dot Labs is a modern web consultancy focused on helping companies realize their digital transformation efforts. For expert architectural guidance, training, or consulting in React, Angular, Vue, Web Components, GraphQL, Node, Bazel, or Polymer, visit [thisdotlabs.com](https://www.thisdotlabs.com).*

*This Dot Media is focused on creating an inclusive and educational web for all.  We keep you up to date with advancements in the modern web through events, podcasts, and free content. To learn, visit [thisdot.co](https://www.thisdot.co).*