Getting to grips with state
with NgRx and RxJS
Ferdinand Malcher and Johannes Hoppe
Prepare for the workshop
ac18.angular.schule
Getting to grips with state
with NgRx and RxJS
Ferdinand Malcher and Johannes Hoppe
Prepare for the workshop
ac18.angular.schule
How can I …
@fmalcher01 | @johanneshoppe
CENTRALIZE
ALL YOUR DATA
@fmalcher01 | @johanneshoppe
State Management
@fmalcher01 | @johanneshoppe
“Redux is a predictable state container for JavaScript apps.” — redux.js.org
@fmalcher01 | @johanneshoppe
Reactive Extensions
for Angular (NgRx)
“RxJS powered state management for Angular applications, inspired by Redux”
— ngrx.github.io
@fmalcher01 | @johanneshoppe
Store
State
immutable
State
immutable
State
immutable
read-only
write: new state
Event
Single Source of Truth
@fmalcher01 | @johanneshoppe
„
“
NgRx provides robust state management for small and large projects. It enforces proper separation of concerns. Using it from the start reduces the risk of spaghetti when the project evolves.
@fmalcher01 | @johanneshoppe
Ferdinand Malcher
@fmalcher01
Johannes Hoppe
@JohannesHoppe
@fmalcher01 | @johanneshoppe
Building Blocks
Action
Reducer
Selector
Effect
Domain event
can trigger state changes
Read data from the state tree
for usage in components
Compute actions
to new state
Trigger side effects
and dispatch new actions
@fmalcher01 | @johanneshoppe
Data Flow in NgRx (basic)
Selector
State
Reducer
dispatch
Action
Component
Store
@fmalcher01 | @johanneshoppe
@fmalcher01 | @johanneshoppe
Prerequisites
Node.js and NPM
Visual Studio Code
Git
@fmalcher01 | @johanneshoppe
Installation
@fmalcher01 | @johanneshoppe
From Zero to NgRx
@fmalcher01 | @johanneshoppe
Setup store and feature reducer
ng g @ngrx/schematics:store State --root --module app
ng g @ngrx/schematics:reducer Book
--group --reducers reducers/index.ts
@fmalcher01 | @johanneshoppe
Structure of the feature state
export interface State {
books: Book[];
loading: boolean;
}
We need to define an initial state in the same file.
book.reducer.ts
@fmalcher01 | @johanneshoppe
„
“
Programming with NgRx is essentially programming with messages
@fmalcher01 | @johanneshoppe
Action
Domain event or command
can trigger state change
User actions/commands
asynchronous events (API calls, timers, …)
@fmalcher01 | @johanneshoppe
Action
Action Object
Type and optional payload
Action Creator Class
{
type: 'LOAD_SUCCESS',
payload: /* ... */
}
export class LoadSuccess implements Action {
readonly type = 'LOAD_SUCCESS';
constructor(public payload: Book[]) {}
}
@fmalcher01 | @johanneshoppe
Action Enum
Avoid magic strings
Group action types in an enum
export enum BookActionTypes {
LoadSuccess = 'LOAD_SUCCESS'
}
Use the enum values
Instead of the magic string
export class LoadSuccess {
readonly type = BookActionTypes.LoadSuccess;
constructor(public payload: Book[]) {}
}
@fmalcher01 | @johanneshoppe
Actions for our application
LoadBooksFail
“An error occured during loading of the book list”
LoadBooksSuccess
“Book list arrived back from the server”
LoadBooks
“Start loading of book list”
@fmalcher01 | @johanneshoppe
Create actions
ng g @ngrx/schematics:action Book --group
…and then build all other actions manually.
see also #1016
@fmalcher01 | @johanneshoppe
Dispatch action
constructor(private store: Store<State>) { }
// ...
this.store.dispatch(new LoadBooks());
dashboard.component.ts
@fmalcher01 | @johanneshoppe
Redux Dev Tools
@fmalcher01 | @johanneshoppe
State
Reducer
dispatch
Action
Component
Store
@fmalcher01 | @johanneshoppe
Reducer
take current state and new action
create new state (immutable)
Staten-1
Reducer
Action
Staten
@fmalcher01 | @johanneshoppe
Reducers are Pure Functions!
Always return a new, cloned state
Spread operator is our best friend
{ ...state }
@fmalcher01 | @johanneshoppe
What are we going to do?
Change the state!
{
books: [/***/],
loading: false
}
{
loading: true
}
LoadBooks
LoadBooksSuccess
@fmalcher01 | @johanneshoppe
Reducer
export function reducer(state, action: BookActions): State {
switch (action.type) {
case BookActionTypes.LoadBooks: {
return { ...state, loading: true }; // return new state
}
default: { return state; }
}
}
book.reducer.ts
@fmalcher01 | @johanneshoppe
Selector
State
Reducer
dispatch
Action
Component
Store
@fmalcher01 | @johanneshoppe
Selector
read part of the state tree
for usage in components
“A selector is a pure function that takes the state as an argument and returns a slice of the store state.”
�— https://blog.angularindepth.com/c50b1dc556bc
�false
{
books: [],
loading: false
}
@fmalcher01 | @johanneshoppe
Feature Selector
Feature Selector
Root selector for one feature – our selector journey always starts here
import { createFeatureSelector } from '@ngrx/store';
import { State as BookState } from './book.reducer';
// ...
export const getBookState = createFeatureSelector<BookState>('book');
book.selectors.ts
@fmalcher01 | @johanneshoppe
Create Selector
import { createSelector } from '@ngrx/store';
// ...
const getBooksLoading = createSelector(
getBookState, // other selectors
state => state.loading // projector function
);
Map state tree to the desired data
Function createSelector()
Projector function as last argument
book.selectors.ts
@fmalcher01 | @johanneshoppe
Use Selector
{
books: [],
loading: false
}
{
books: [],
loading: true
}
{
books: [/***/],
loading: true
}
{
books: [/***/],
loading: false
}
select(getBooksLoading)
�true
�false
�false
Memoization
@fmalcher01 | @johanneshoppe
Use Selectors in component
loading$ = this.store.pipe(
select(getBooksLoading)
);
books$ = this.store.pipe(
select(getAllBooks)
);
dashboard.component.ts
@fmalcher01 | @johanneshoppe
Data flow in NgRx (complete)
HTTP
Web Server
Effect
dispatch
Selector
Action
dispatch
State
Reducer
Action
Component
Store
@fmalcher01 | @johanneshoppe
Effect
Reactive data stream (Observable of Actions)
React to actions and other events
Trigger side effects (everything is allowed)
Automatically dispatch new action
Action
Action
@fmalcher01 | @johanneshoppe
Create effects class
ng g @ngrx/schematics:effect Book --group --root --module app
@fmalcher01 | @johanneshoppe
What are we going to do?
React to the LoadBooks action
Fetch books from the server
Dispatch LoadBooksSuccess with the book list inside
@fmalcher01 | @johanneshoppe
Effect
@Effect()
loadBooks$ = this.actions$.pipe(
ofType(BookActionTypes.LoadBooks), // filter actions
concatMap(action => this.service.getAll()), // Side Effect HTTP
tap(action => console.log('Logging')), // Side Effect Logging
map(books => new LoadBooksSuccess(books)) // map to action
);
book.effects.ts
The resulting Action will be dispatched automatically!
@fmalcher01 | @johanneshoppe
Data flow in NgRx (complete)
HTTP
Web Server
Effect
dispatch
Selector
Action
dispatch
State
Reducer
Action
Component
Store
@fmalcher01 | @johanneshoppe
SHARI: What belongs in the Store?
State that is accessed by many components and services
State that is persisted and [later on] hydrated from storage
State that needs to be available when re-entering routes
State that needs to be retrieved with a side effect
State that is impacted by actions from other sources
Shared
Hydrated
Available
Retrieved
Impacted
@fmalcher01 | @johanneshoppe
How can I do…
Routing
Bindings to connect the Angular Router with @ngrx/store
NGRX Router – Router Bindings and Helpers for NgRx Effects
@fmalcher01 | @johanneshoppe
How can I do…
Testing
Testing Effects
@ngrx/effects/testing provides utilities for effect(ive) testing
Marble Testing
Testing RxJS Code with Marble Diagrams
@fmalcher01 | @johanneshoppe
How can I do…
Entity Management
Entity State adapter for managing record collections
Radically reduce the amount of "boilerplate" necessary to manage entities with NgRx
@fmalcher01 | @johanneshoppe
What can you do?
@fmalcher01 | @johanneshoppe
Read blogs
Watch talks
Resources list in our guide document
ac18.angular.schule
@fmalcher01 | @johanneshoppe
Do try this at home
We prepared some homework for you.
@fmalcher01 | @johanneshoppe
Discuss with friends
Ask questions
Stay curious
@fmalcher01 | @johanneshoppe
@angular_schule
@fmalcher01 | @johanneshoppe
Code on GitHub
https://github.com/
angular-schule/book-rating-ngrx
@fmalcher01 | @johanneshoppe
Credits
Network and Database icons�Web Development icons
�designed by Smashicons from flaticon.com�
@fmalcher01 | @johanneshoppe