RxJS Notification and materialize / dematerialize operators
Tags: rxjs, javascript
Published: 2019-07-28 13:28:50

---
title: RxJS Notification and materialize / dematerialize operators
published: true
description: RxJS operators I did not know existed: materialize and dematerialize.
tags: RxJS, JavaScript
---

> This article assumes basic knowledge of RxJS.
> If you are unfamiliar with RxJS, I highly recommend [reading through the docs](https://rxjs-dev.firebaseapp.com/).


Recently, while scrolling through the [RxJS docs](https://rxjs-dev.firebaseapp.com/) looking for inspiration on how to solve one of the problems I was facing, I noticed a type (and subsequent operators that go along with it) that I never used: `Notification`.

Today, I want to share what I learned fiddling with it.

# Notification
[Docs](https://rxjs-dev.firebaseapp.com/) state that `Notification` is:
> [...] a push-based event or value that an Observable can emit. ...  Besides wrapping the actual delivered value, it also annotates it with metadata

Let's try to break it down, piece by piece.

## Event or value that an Observable can emit

`Observable` can emit*
- onComplete
- onError
- onNext

Let's try to replicate this behavior using `Notification` API.

```js
import { Notification, from } from "rxjs";

// Notification(Type, Value?, Error?)

const onErrorNotification = new Notification("E",{}, 'onError' );
const onCompleteNotification = new Notification("C", 'onComplete');
const onNextNotification = new Notification("N", 'onNext');

from([onNextNotification, onCompleteNotification, onErrorNotification])
  .subscribe(console.log);

/* {
 *  kind: 'E' or 'C' or 'N'
 *  value: 'onNext' or 'onComplete' or undefined
 *  error: undefined or 'onError'
 *  hasValue: true or false (onNext hasValue is always true)
 * }
 */

```

I did not use `onComplete` and `onError` handlers to log the values, why?

It's because `Notifications` are treated as `onNext` events, but they (**in this case**) represent the underlying events and values, so `onComplete` and `onError` handlers would never trigger.


## Annotates it (the actual value) with metadata

There is a great deal of information that `Notification` carries: 

- It tells you the type of event (with `kind` prop)
- It exposes to you the actual value (with `value` prop)
- It informs you about the error (with `error` prop), and it's value
- It tells you if the event is *value-based* (`onNext`)

It's all great and all, but how do we actually transform the `Observable` event to a `Notification`?

Enter: `materialize` and `dematerialize`

# `materialize` and `dematerialize`

These operators are pretty interesting.
They allow you to control in which 'realm' of events or values (`Notification` or normal, `Observable` based) you currently reside.

## `dematerialize`

This operator allows you to 'degrade' `Notification` to the underlying `Observable` event or value that given `Notification` was.

Let's remake the first code example so that we actually have to have all 3 handlers attached (`onNext`, `onError`, `onComplete`) to get all data.

```js
from([ onNextNotification, onErrorNotification, onCompleteNotification ])
  // 'degrading' to an Observable-event world
  .pipe(dematerialize())
  .subscribe({
    next: console.log,
    error: console.log,
    complete: console.log
  });

/* onNext
 * onError
 */
```

Why was not `onComplete` logged out?

> Any given stream can only error out once. This is defined by the Observable contract, which says that a stream can emit zero or more values.

In our case, it means that the stream has ended its life cycle with an error and will not emit any further values.

This situation hints on the use case where we want to, despite the error, carry on with our operator chain.

## `materialize`

Just like, `dematerialize` 'degraded' events, `materialize`, enables you to 'promote' given event to a `Notification` type.

Let's say we know our source `Observable` can randomly throw, but we still want to go through our operator chain.

```js
import { dematerialize, materialize, map, delay } from "rxjs/operators";
import { Notification, timer } from "rxjs";

sourceThatThrows$
  .pipe(
    // Promoting the event to a Notification
    materialize(),
    map(notification =>
      // Was the event value based? (onNext, onComplete)
      notification.hasValue
        ? notification
        // As alternative you could also use new Notification(...)
        : Notification.createNext("was error value!")
    ),
    delay(100),
    dematerialize()
  )
  .subscribe(console.log);

/* value from source
 * was error value
 */
```

Using, `materialize` and `dematerialize` operators we successfully preserved our operator chain even though the source can randomly throw.

# Summary
RxJS ecosystem is vast, with 100+ operators there is definitely much to learn. 
I hope, I was able to shed some basic knowledge on these particular two.

You can follow me on twitter [@wm_matuszewski](https://twitter.com/wm_matuszewski)

Thanks 👋


# Footnotes

\* I'm not an expert, there is probably much more stuff that Observable can emit. For the sake of this article, I assumed those three events. 

Edit:
- Thanks to [Christopher Hiller](https://dev.to/boneskull) for pointing out that `Notification` as `onComplete` event has `hasValue` set to false, and therefore it's not *value-based* event.