Pattern with Reactive Programming

Angular

NGRX

  • v6.0.1 (Dec 2015)
  • 122 contributors
  • CLI integration
  • 3K stars
  • v3.1.4 (Jan 2018)
  • 59 contributors
  • Plugins
  • 1K stars
  • v4 (June 2015)
  • 596 contributors
  • Inspired by Flux
  • 42K stars

Overview

  • State Management for Angular
  • Inspired by Redux
  • Implemented using RxJS
  • Angular CLI integration via schematics

@ngrx/store life cycle

source: blog

1

2

3

4

5

actions

store

A

A

A

S

S

S

Components

Actions

Reducer

Match

Dispatch

Creates

State

Notify

Packages

@ngrx/store-devtools

@ngrx/store

@ngrx/schematics

@ngrx/router-store

@ngrx/effects

@ngrx/entity

Features

Utilities

Just RX

 

Different Patterns RX

Components

Services

Subject

BehaviouralSubjects

  • State less Reactive Pattern
  • Mini Store Component Level using Observables
  • Data-Driven using | async Pipe
  • No need to manage Subscription
  • Non-Ngrx Solution

 

import {Observable, BehaviorSubject} from 'rxjs';

export class Store<T> {
    state$: Observable<T>;
    private _state$: BehaviorSubject<T>;
    protected constructor (initialState: T) {
        this._state$ = new BehaviorSubject(initialState);
        this.state$ = this._state$.asObservable();
    }
    get state (): T {
        return this._state$.getValue();
    }
    setState (nextState: T): void {
        this._state$.next(nextState);
    }
}
@Injectable()
export class ElectionStore extends Store<ElectionState> {
  constructor () {
    super(new ElectionState());
  }
  addVote (candidate: {name: string, votes: number}): void {
    this.setState({
      ...this.state,
      candidates: this.state.candidates.map(c => {
        if (c === candidate) {
          return {...c, votes: c.votes + 1};
        }
        return c;
      })
    });
  }
}
const CANDIDATES = [
  {name: 'Americano', votes: 5},
  {name: 'Cappuccino', votes: 4}
];
export class ElectionState {
  candidates: {name: string, votes: number}[] = CANDIDATES;
}
<h2>Candidates</h2>
<ul>
  <li *ngFor="let candidate of (store.state$ | async).candidates">
    <span>{{ candidate.name }}</span>
    <span>Votes: {{ candidate.votes }}</span>
    <button (click)="store.addVote(candidate)">+</button>
  </li>
</ul>

Stateless Observables

@Injectable({
  providedIn: 'root'
})
export class PostsService {
  private postUrl = 'https://jsonplaceholder.typicode.com/posts';
  posts: Posts = [];

  post$ = this.http.get<Posts>(this.postUrl).pipe(
    shareReplay()
  );
  constructor(private http: HttpClient) { }
}
export class UsersService {
  constructor(private http: HttpClient, private postsService: PostsService) { }

  private userSelectedAction = new Subject<number>();
  private usersUrl = 'https://jsonplaceholder.typicode.com/users';
  user$ = this.http.get<Users>(this.usersUrl).pipe(shareReplay());
  userWithPost$ = combineLatest(
    this.user$,
    this.postsService.post$
  ).pipe( map( ([users, posts]) => {
      // TODO
  }));
}
	<div class="block">
		<h1>List of users</h1>
		<ul *ngIf="userWithPost$ | async as userList">
			<li *ngFor="let user of userList">
				<a href="#" (click)="selectUser(user.id)">{{user.username}}</a>
			</li>
		</ul>
	</div>
  • Can be used with onPush change detection strategy
  • Provides mini Store Pattern and stateless pattern as well
  • No need to worry about unsubscribe

RX JS Patterns

By Tarun Sharma

RX JS Patterns

State Management is key to build modern Web Apps

  • 305