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