DEV Community

Henri de la Hoz
Henri de la Hoz

Posted on

Reactivity with RXjs

Observables vs Promises

a promise performs a fetch and we can use then method to wait for the response, in recent years we have available the keywords async and await to process the response.
Observable uses a different approeach, in this case we work witha data stream and then we subscribe to it. As soon as we get new data comming from the stream our subscription is going to be notify and we will have the incoming data available.

By definition, promises are called one time and are resolved once.
On the other hands, observables allow us to stream data, send data multiple times.
Observables allow us to cancel them and we can manipulate the data stream using pipes.

Angular uses observable in its core to process http requests, routing, changes in the window, etc.

RXjs

RxJS is a library that allows us to handle reactivity all over the app via subscription publisher pattern.

One common class that we can use to implement this functionality is BehaviorSubject class

import { Injectable } from "@angular/core";
import { BehaviorSubject } from "rxjs";
import { Product } from "./../../core/models/product.model";

@Injectable({
  providedIn: "root",
})
export class CartService {
  // This value is the dynamic value, we want to react to its changes
  private products: Product[] = [];
  // This BehaviorSubject instance defines the way reactivity is going to be hanle
  private cart = new BehaviorSubject<Product[]>([]);
  // Observable object consume by subscriptors
  cart$ = this.cart.asObservable();

  constructor() {}

  addToCart(product: Product) {
    // setting new value
    this.products = [...this.products, product];
    // sending new value to suscriptors
    this.cart.next(this.products);
  }
}
Enter fullscreen mode Exit fullscreen mode

addToCart method can be used from any component injecting the service.

Now how can we subscribe to this reactive class?

import { Component, OnInit } from '@angular/core';
import { map } from 'rxjs/operators';
import { CartService } from '@core/services/cart.service';
import { Observable } from 'rxjs';

@Component({
  selector: 'app-header',
  templateUrl: './header.component.html',
  styleUrls: ['./header.component.scss'],
})
export class HeaderComponent implements OnInit {
  total$: Observable<number>;

  constructor(private cartService: CartService) {
    this.total$ = this.cartService.cart$.pipe(
      map((products) => products.length;)
    );
  }

  ngOnInit(): void {}
}
Enter fullscreen mode Exit fullscreen mode

Using switchMap to reduce the number of subscriptions

Code we want to optimize

product: Product;

ngOnInit(): void {
    this.route.params.subscribe((params: Params) => {
        const id = params.id;
        this.fetchProduct(id);
    });
}

fetchProduct(id: string) {
    this.productsService.getProduct(id).subscribe((product) => {
        this.product = product;
    });
}
Enter fullscreen mode Exit fullscreen mode

Optimized code

product$: Observable<Product>;

constructor(
    private route: ActivatedRoute,
    private productsService: ProductsService
) {}

ngOnInit(): void {
    this.product$ = this.route.params.pipe(
        switchMap(
            (params: Params) => this.productsService.getProduct(params.id)
            )
        );
}
Enter fullscreen mode Exit fullscreen mode

Consuming the observable in a component

<div *ngIf="(product$ | async) as product">
Enter fullscreen mode Exit fullscreen mode

Handling errors in subscriptions

subscribe(successFunction, (error) => {
  handlingError();
});
Enter fullscreen mode Exit fullscreen mode

We can use the function handlingError to show logs or event store the event or send it to sentry, etc.

Top comments (0)