SlideShare una empresa de Scribd logo
1 de 97
Descargar para leer sin conexión
NGRXNGRX
THERE IS A REDUCER IN MY SOUPTHERE IS A REDUCER IN MY SOUP
1
About me
Google Developer Expert
Telerik Developer Expert
Digital McKinsey
@chris_noring
2
NGRX IS:NGRX IS:
An Angular implementation of Redux
3 . 1
Why do we care about Redux?
If you experience the following symtoms you might
need Redux:
A feeling that the state is spread out
There are issues with updating state
Some things keep changing the state but you don't
know who or what
Un an explainable itch
3 . 2
Solution: a single source of truth with reducers
guarding state change. Also enhanced predictability
with immutable data structures
3 . 3
CORE CONCEPTSCORE CONCEPTS
3 . 4
Store, our data store
Reducer, a function that takes state + action and
produces a new state
Selector, selects a slice of state
Action, an intention of state change
Action creator, produces an intention that may
include data
3 . 5
Typical Store content, just an object
{
counter: 'a value',
jedis: [{ id: 1, name: 'Yoda' }],
selectedJedi: { id: 1, name: 'Yoda' }
}
3 . 6
REDUCERREDUCER
NEW STATE = STATE + ACTIONNEW STATE = STATE + ACTION
3 . 7
Let's take some reducing examples:
BANANA + MILK + ICE CREAM =BANANA + MILK + ICE CREAM =
MILKSHAKEMILKSHAKE
PIZZA + HAMBURGER + ICECREAM =PIZZA + HAMBURGER + ICECREAM =
STOMACH ACHESTOMACH ACHE
3 . 8
Mathematical function, immutable, just a calculation
Immutable = Predictability
//mutating
var sum = 3;
function add(a) { sum += a; return sum; }
add(5); // 8
add(5); // 13
// immutable
function computeSum(a,b) { return a + b; }
computeSum(1,1); // 2
computeSum(1,1); // 2
3 . 9
A reducer looks like the following:
function reducer(state, action) { /* implementation */ }
state, previous/initial state
action = {
type: 'my intent, e.g ADD_ITEM',
payload: { /* some kind of object */ }
}
3 . 10
state + 1, instead of state +=1, immutable
function counterReducer(state = 0, action) {
switch(action.type) {
case 'INCREMENT':
return state + 1;
default:
return state;
}
}
3 . 11
Usage
let initialState = 0;
let state = counterReducer(initialState,{ type: 'INCREMENT' })
// 1
state = counterReducer(state, { type: 'INCREMENT' })
// 2
function counterReducer(state = 0, action) {
switch(action.type) {
case 'INCREMENT':
return state + 1;
default:
return state;
}
}
3 . 12
A list reducer
function jediReducer(state = [], action) {
switch(action.type) {
case 'ADD_JEDI':
return [ ...state, action.payload];
case 'REMOVE_JEDI':
return state.filter(
jedi => jedi.id !== action.payload.id);
default:
return state;
}
}
3 . 13
Usage, list reducer
let state = jediReducer([], {
type: 'ADD_JEDI',
payload: { id: 1, name: 'Yoda' }
});
// [{ id: 1, name: 'Yoda' }]
state = jediReducer(state, {
type: 'REMOVE_JEDI',
payload: { id: 1 }
});
// []
3 . 14
An object reducer
Build a new object based on old + change with
...spread
let initialState = {
loading: false,
data: [],
error: void 0
};
function productsReducer(state = initialState, action) {
switch(action.type) {
case 'FETCHING_PRODUCTS':
return { ...state, loading: true };
case 'FETCHED_PRODUCTS':
return { ...state, data: action.payload, loading: false
case 'FETCHED_PRODUCTS_ERROR':
return { ...state, error: action.payload, loading: false
}
}
3 . 15
Object reducer, usage
Handling an AJAX case, can show spinner when,
loading = true
let state = productsReducer(initialState, {
type: 'FETCHING_PRODUCTS'
});
try {
let products = await getProducts(); // from an endpoint;
state = productsReducer(state, {
type: 'FETCHED_PRODUCTS',
payload: products
});
} catch (error) {
state = productsReducer(state, {
type: 'FETCHED_PRODUCTS_ERROR',
payload: error
});
}
3 . 16
A simple store
class Store {
constructor(initialState) { this.state = initialState; }
dispatch(action) {
this.state = calc(this.state, action);
}
calc(state, action) {
return {
counter: counterReducer(state.counter, action),
jedis: jediReducer(state.jedis, action)
}
}
}
3 . 17
Usage, store
let store = new Store({ counter: 0, jedis: [] });
store.dispatch({ type: 'INCREMENT' });
// { counter: 1, jedis: [] }
store.dispatch({
type: 'ADD_JEDI',
payload: { id: 1, name: 'Yoda' }
});
// { counter: 1, jedis: [{ id: 1, name: 'Yoda' }] }
store.dispatch({
type: 'REMOVE_JEDI',
payload: { id: 1 }
});
// { counter: 1, jedis: [] }
3 . 18
Action, an object with a property 'type' and 'payload'
'type' = intent
'payload' = the change
{ type: 'ADD_JEDI', payload: { id: 1, name: 'Yoda' } }
3 . 19
Action creator, function that creates action
const addJedi = (id, name) =>
({ type: 'ADD_JEDI', payload: { id, name } });
addJedi(1, 'Yoda');
// { type:'ADD_JEDI', payload: { id: 1, name: 'Yoda' } }
//usage
store.dispatch(addJedi(1, 'Yoda'));
3 . 20
Selector, slice of state
class Store {
constructor(initialState) { ... }
dispatch(action) { ... }
calc(state, action) { ... }
select(fn) {
return fn(state);
}
}
3 . 21
Selector, definitions
const getCounter = (state) => state.counter;
const getJedis = (state) => state.jedis;
3 . 22
NGRXNGRX
OVERVIEW OF LIBRARIESOVERVIEW OF LIBRARIES
4 . 1
@ngrx/store, the store
@ngrx/store-devtools, a debug tool that helps you
track dispatched actions
@ngrx/router-store, lets you put the routing state in
the store
@ngrx/effects, handles side effects
@ngrx/entites, handles records
@ngrx/schematics
4 . 2
STORESTORE
WHERE THE STATE LIVESWHERE THE STATE LIVES
5 . 1
INSTALLATION AND SET UPINSTALLATION AND SET UP
npm install @ngrx/store --save
// file 'app.module.ts'
import { StoreModule } from '@ngrx/store';
import { counterReducer } from './counter.reducer';
@NgModule({
imports: {
StoreModule.forRoot({
counter: counterReducer
})
}
})
export class AppModule {}
5 . 2
SHOW DATA FROM STORESHOW DATA FROM STORE
// app-state.ts
export interface AppState {
counter: number;
}
// some.component.ts
@Component({
template: ` {{ counter$ | async }} `
})
export class SomeComponent {
counter$;
constructor(this store:Store<AppState>) {
this.counter$ = this.store.select('counter');
}
}
5 . 3
SHOW DATA FROM STORE, SELECTORSHOW DATA FROM STORE, SELECTOR
FUNCTIONFUNCTION
// app-state.ts
export interface AppState {
counter: number;
}
// some.component.ts
@Component({
template: ` {{ counter$ | async }} `
})
export class SomeComponent {
counter$;
constructor(this store:Store<AppState>) {
this.counter$ = this.store
.select( state => state.counter);
}
}
5 . 4
DISPATCH DATADISPATCH DATA
@Component({
template: `
{{ counter$ | async }}
<button (click)="increment()">Increment</button>
<button (click)="decrement()">Decrement</button>
`
})
export class SomeComponent {
counter$;
constructor(this store:Store<AppState>) {
this.counter$ = this.store.select('counter');
}
increment() { this.store.dispatch({ type: 'INCREMENT' }); }
decrement() { this.store.dispatch({ type: 'DECREMENT' }); }
}
5 . 5
DISPATCH DATA, WITH PAYLOAD,DISPATCH DATA, WITH PAYLOAD,
TEMPLATETEMPLATE
@Component({
template: `
<input [(ngModel)]="newProduct" />
<div *ngFor="let product of products$ | async">
{{ product.name }}
<button (click)="remove(product.id)">Remove</button>
</div>
<button (click)="add()">Add</button>
`
})
5 . 6
CLASS BODYCLASS BODY
export class SomeComponent {
products$, id = 0;
constructor(this store:Store<AppState>) {
this.counter$ = this.store.select('products');
}
remove(id) { this.state.dispatch({
type: 'REMOVE_PRODUCT', payload: { id } })
}
add() {
this.state.dispatch({
type: 'ADD_PRODUCT',
payload: { id : this.id++, this.name: newProduct }
})
}
}
5 . 7
When our app grows, we can't have all the state in
StoreModule.forRoot({})
Solution is using StoreModule.forFeature()
Let's also find a way to organize files
5 . 8
Set up store in a feature module,
StoreModule.forFeature('feature',{})
interface CombinedState {
list: Product[];
item: Product;
};
const combinedReducers: ActionReducerMap<CombinedState> = {
list: listReducer,
item: itemReducer
};
@NgModule({
imports: [
StoreModule.forFeature<CombinedState, Action>(
'products',
combinedReducers
)]
});
5 . 9
Organizing your files, Domain approach
/feature
feature.component.ts
feature.selector.ts
feature.actions.ts
feature.reducer.ts
5 . 10
Organizing your files, Ruby on Rails approach
/feature
feature.component.ts
/feature2
feature2.component.ts
/reducers
feature.reducer.ts
feature2.reducer.ts
index.ts
/selectors
feature.selector.ts
feature2.selector.ts
index.ts
/actions
feature.actions.ts
feature2.actions.ts
5 . 11
Whatever approach you go for, consistency is key
5 . 12
STORE DEVTOOLSSTORE DEVTOOLS
DEBUG LIKE A PRODEBUG LIKE A PRO
6 . 1
INSTALLATION AND SET UPINSTALLATION AND SET UP
Install lib on NPM and download chrome extension on
http://extension.remotedev.io/
6 . 2
npm install @ngrx/store-devtools
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
@NgModule({
imports: [
StoreDevtoolsModule.instrument({
maxAge: 25 // Retains last 25 states
}
)]
})
6 . 3
What can we do with it?
See dispatched actions
Undo dispatched actions
Use time travel debugging and move through time,
back and forth with a gauge
6 . 4
Initial view, we see actions as well as different tabs
6 . 5
Here we can see the detail of an action, what type etc.
6 . 6
It records all of our actions, here multiple dispatched
actions
6 . 7
Here we are undoing/skipping an action, store is
recalculated
6 . 8
This gauge enables us to travel through time, back and
forth
6 . 9
ROUTER STOREROUTER STORE
ENABLING US TO PUT THE ROUTERENABLING US TO PUT THE ROUTER
STATE IN THE STORESTATE IN THE STORE
7 . 1
We want to accomplish the following:
Save the route state to our store
Customize whats gets saved
down
7 . 2
INSTALLATION AND SET UPINSTALLATION AND SET UP
npm install @ngrx/router-store
import { StoreRouterConnectingModule } from '@ngrx/router-stor
@NgModule({
imports: [
StoreModule.forRoot({
router: routerReducer // this is where our route state g
}),
StoreRouterConnectingModule.forRoot({
stateKey: 'router' // name of reducer key
})
]
})
7 . 3
We can listen to this 'router' state like any other state
@Component({
template: ``
})
export class SomeComponent {
constructor(private state: State<AppState>) {
// updates every time we route
this.state.select('router')
.subscribe(data => console.log(data));
}
}
7 . 4
Might be a bit hard to read
7 . 5
LET'S BUILD OUR OWN ROUTER STATELET'S BUILD OUR OWN ROUTER STATE
7 . 6
The following is of interest:
The url
The router
parameters
The query parameters
7 . 7
Define a serializer, that saves url, queryParams and
router params
interface MyState {
url: string;
queryParams;
params;
}
export class MySerializer implements
RouterStateSerializer<MyState> {
serialize(routerState: RouterStateSnapshot): MyState {
console.log('serializer');
console.log('complete router state', routerState);
const { url, root: { queryParams, firstChild: { params } }
return { url, queryParams, params };
}
}
7 . 8
Provide this as the 'new' serializer
@NgModule({
providers: [{
provide: RouterStateSerializer,
useClass: MySerializer
}]
})
7 . 9
This is what the router state looks like now, only saves
exactly what we want
7 . 10
EFFECTSEFFECTS
HANDLING SIDE EFFECTSHANDLING SIDE EFFECTS
8 . 1
Objective: We want to ensure we can carry out things
like accessing resources over the network.
8 . 2
INSTALLATION AND SETUPINSTALLATION AND SETUP
npm install @ngrx/effects
import { EffectsModule } from '@ngrx/effects';
@NgModule({
EffectsModule.forRoot([ ... my effects classes ])
})
8 . 3
What kind of behaviour do we want?
Set a loading flag, show spinner
Do AJAX call
Show fetched data or error
Set loading flag to false, hide
spinner
8 . 4
NGRX approach
try {
store.dispatch({ type: 'FETCHING_DATA' })
// state: { loading: true }
const data = await getData(); // async operation
store.dispatch({ type: 'FETCHED_DATA', payload: data });
// state: { loading: false, data: {/* data from endpoint */}
} catch (error) {
store.dispatch({
type: 'FETCHED_DATA_ERROR',
payload: error
});
}
8 . 5
My first effect
@Injectable()
export class ProductEffects {
@Effect()
products$: Observable<Action> = this.actions$.pipe(
ofType(FETCHING_PRODUCTS),
switchMap(
// ensure we dispatch an action the last thing we do
action => of({ type: "SOME_ACTION" })
)
);
constructor(private actions$: Actions<Action>, private http:
console.log("product effects init");
}
}
8 . 6
My first effect - calling HTTP
@Injectable()
export class ProductEffects {
@Effect()
products$: Observable<Action> = this.actions$.pipe(
ofType(FETCHING_PRODUCTS), // listen to this action
switchMap(action =>
this.http
.get("data/products.json")
.pipe(
delay(3000),
map(fetchProductsSuccessfully), // success
catchError(err => of(fetchError(err))) // error
)
)
);
8 . 7
ENTITESENTITES
REDUCE THAT BORING BOILER PLATEREDUCE THAT BORING BOILER PLATE
9 . 1
Install and set up
npm install @ngrx/entity
import {
EntityState,
createEntityAdapter,
EntityAdapter } from "@ngrx/entity";
// set up the adapter
const adapter: EntityAdapter<User> =
createEntityAdapter<User>();
// set up initial state
const initial = adapter.getInitialState({
ids: [],
entities: {}
});
9 . 2
Install and set up
/*
use the adapter methods
for specific cases like adapter.addOne()
*/
function userReducer(
state = initial,
action: ActionPayload<User>
) {
switch (action.type) {
case "ADD_USER":
return adapter.addOne(action.payload, state);
default:
return state;
}
}
9 . 3
What else can it do for us?
addOne: Add one entity to the collection
addMany: Add multiple entities to the collection
addAll: Replace current collection with provided
collection
removeOne: Remove one entity from the collection
removeMany: Remove multiple entities from the
collection
removeAll: Clear entity collection
updateOne: Update one entity in the collection
updateMany: Update multiple entities in the
collection 9 . 4
Further reading:
https://github.com/ngrx/platform/tree/master/docs/ent
9 . 5
SCHEMATICSSCHEMATICS
BE LAZY AND SCAFFOLD :)BE LAZY AND SCAFFOLD :)
10 . 1
It is a scaffolding library that helps us scaffold out
NGRX features
10 . 2
Schematics can help us scaffold the following:
Action
Container
Effect
Entity
Feature
Reducer
Store
10 . 3
Install and set up
// install schematics and prerequisits
npm install @ngrx/schematics --save-dev
npm install @ngrx/{store,effects,entity,store-devtools} --save
// we might need this one as well
npm install --save @angular/cli@latest
10 . 4
Scaffold, initial state set up, forRoot({}) and
instrument()
ng generate store State --root --module app.module.ts --collec
// result
@NgModule({
declarations: [ ... ],
imports: [
BrowserModule,
StoreModule.forRoot(reducers, { metaReducers }),
!environment.production ? StoreDevtoolsModule.instrument()
],
bootstrap: [AppComponent]
})
export class AppModule { }
10 . 5
Scaffold, setting up effects for the App
ng generate effect App --root --module app.module.ts --collect
@NgModule({
declarations: [
AppComponent
],
imports: [
...
EffectsModule.forRoot([AppEffects])
],
providers: [],
bootstrap: [ ... ]
})
export class AppModule { }
10 . 6
Scaffold, create Action, generates action and test
ng generate action User --spec
export enum UserActionTypes {
UserAction = '[User] Action'
}
export class User implements Action {
readonly type = UserActionTypes.UserAction;
}
export type UserActions = User;
10 . 7
Scaffold, create a component, with store injected
ng generate container TodoList
@Component({
selector: 'app-todo-list',
templateUrl: './todo-list.component.html',
styleUrls: ['./todo-list.component.css']
})
export class TodoListComponent implements OnInit {
constructor(private store: Store<any>) { }
ngOnInit() {
}
}
10 . 8
DO IT YOURSELFDO IT YOURSELF
BUILD YOUR OWN NGRXBUILD YOUR OWN NGRX
11 . 1
What requirements do we have?
Should be possible to dispatch actions
State should update when we dispatch
It should be possible to subscribe to a slice of
state
We should support side effects
11 . 2
BEHAVIOURSUBJECTBEHAVIOURSUBJECT
SUPPORTS A STATE, CAN BESUPPORTS A STATE, CAN BE
SUBSCRIBED TOSUBSCRIBED TO
11 . 3
BehaviorSubject
// ctor value = inital value
let subject = new BehaviorSubject({ value: 1 })
subject.subscribe(data => console.log('data', data));
// { value: 1 }
// { prop: 2}
subject.next({ prop: 2 });
11 . 4
Merging states with .scan()
let subject = new BehaviorSubject({ value: 1 }) // {} inital
subject
.scan((acc, value) =>({ ...acc, ...value }))
.subscribe(data => console.log('data', data));
subject.next({ prop: 2 }); // { value: 1, prop: 2 }
11 . 5
Implementing the store
class Store extends BehaviorSubject {
constructor(initialState = {}) {
super(initialState);
this.listenerMap = {};
this.dispatcher = new Subject();
this.dispatcher
.scan((acc, value) =>({ ...acc, ...value }))
.subscribe(state => super.next(state));
}
dispatch(newState) {
this.dispatcher.next(newState);
}
}
11 . 6
Usage, store
store = new Store();
store.subscribe(data => console.log('state', data));
store.dispatch({ val: 1 });
store.dispatch({ prop: 'string' });
// { val: 1, prop: 'string' }
11 . 7
What about slice of state?
class Store extends BehaviorSubject {
constructor(initialState = {}) {
...
}
dispatch(newState) {
this.dispatcher.next(newState);
}
select(slice) { return this.map[slice] }
selectWithFn(fn) { return this.map(fn) }
}
11 . 8
We need to improve the core implementation, enter
storeCalc()
const storeCalc = (state, action) => {
return {
counter: countReducer(state.counter, action),
products: productsReducer(state.products, action)
}
};
11 . 9
A retake on our dispatch(), old state, getValue() +
action = new state
dispatch(action) {
const newState = storeCalc(this.getValue(),action);
this.dispatcher.next(newState);
}
11 . 10
LETS' TALK ABOUT EFFECTSLETS' TALK ABOUT EFFECTS
11 . 11
We need to be able to signup to specific
actions
We need to be able to carry out side effects
11 . 12
First let's set up subscription in the store
class Store {
constructor() { ... }
dispatch() { ... }
select() { ... }
effect(listenToAction, listener) {
if(!this.listenerMap.hasOwnProperty(listenToAction)) {
this.listenerMap[listenToAction] = [];
}
this.listenerMap[listenToAction].push( listener );
}
}
11 . 13
Then ensure the effect happens in dispatch()
class Store {
constructor() { ... }
dispatch() {
const newState = storeCalc(this.getValue(),action);
this.dispatcher.next(newState);
// tell our listeners this action.type happened
if(this.listenerMap[action.type]) {
this.listenerMap[action.type].forEach(listener => {
listener(this.dispatch.bind(this),action);
});
}
}
select() { ... }
effect(listenToAction, listener) { ... }
}
11 . 14
use our new effect() method
let store = new Store();
store.effect('INCREMENT' ,async(dispatch, action) => {
// side effect
let products = await getProducts();
// side effect
let data = await getData();
// dispatch, if we want
dispatch({ type: 'INCREMENT' });
})
store.dispatch({ type: 'DECREMENT' });
11 . 15
SUMMARYSUMMARY
12 . 1
We learned how to:
Grasp the basics of Redux
NGRX building blocks
Use the store
Leverage the dev tools and its Redux plugin
Store our router state and transform it
How we handle side effect like AJAX calls
Remove boiler plate with Entity
How to be even lazier with the scaffold tool
Schematics
Upgrading ourselves to Ninja level by learning how
to implement NGRX
12 . 2
Further reading:
Free video course,
https://platform.ultimateangular.com/courses/ngrx-st
effects
Redux docs, https://redux.js.org/docs
Probably the best homepage on it, Brian Troncone,
https://gist.github.com/btroncone/a6e4347326749f93
12 . 3
Buy my book ( please :) ):
https://www.packtpub.com/web-
development/architecting-angular-applications-flux-
redux-ngrx
12 . 4
Thank you for listening
12 . 5

Más contenido relacionado

La actualidad más candente

La actualidad más candente (20)

Introduction to react_js
Introduction to react_jsIntroduction to react_js
Introduction to react_js
 
React JS part 1
React JS part 1React JS part 1
React JS part 1
 
Angular Data Binding
Angular Data BindingAngular Data Binding
Angular Data Binding
 
Reactjs
ReactjsReactjs
Reactjs
 
WEB DEVELOPMENT USING REACT JS
 WEB DEVELOPMENT USING REACT JS WEB DEVELOPMENT USING REACT JS
WEB DEVELOPMENT USING REACT JS
 
Introduction to React JS
Introduction to React JSIntroduction to React JS
Introduction to React JS
 
Understanding react hooks
Understanding react hooksUnderstanding react hooks
Understanding react hooks
 
react redux.pdf
react redux.pdfreact redux.pdf
react redux.pdf
 
Angular Lifecycle Hooks
Angular Lifecycle HooksAngular Lifecycle Hooks
Angular Lifecycle Hooks
 
Advanced Javascript
Advanced JavascriptAdvanced Javascript
Advanced Javascript
 
React js
React jsReact js
React js
 
Jetpack Compose.pptx
Jetpack Compose.pptxJetpack Compose.pptx
Jetpack Compose.pptx
 
React js programming concept
React js programming conceptReact js programming concept
React js programming concept
 
Angular 2 observables
Angular 2 observablesAngular 2 observables
Angular 2 observables
 
React JS - Introduction
React JS - IntroductionReact JS - Introduction
React JS - Introduction
 
React hooks
React hooksReact hooks
React hooks
 
BDD with CucumberJS and WebdriverIO
BDD with CucumberJS and WebdriverIOBDD with CucumberJS and WebdriverIO
BDD with CucumberJS and WebdriverIO
 
Declarative UIs with Jetpack Compose
Declarative UIs with Jetpack ComposeDeclarative UIs with Jetpack Compose
Declarative UIs with Jetpack Compose
 
Angular Observables & RxJS Introduction
Angular Observables & RxJS IntroductionAngular Observables & RxJS Introduction
Angular Observables & RxJS Introduction
 
Workshop 21: React Router
Workshop 21: React RouterWorkshop 21: React Router
Workshop 21: React Router
 

Similar a Ngrx slides

Intro to Redux | DreamLab Academy #3
Intro to Redux | DreamLab Academy #3 Intro to Redux | DreamLab Academy #3
Intro to Redux | DreamLab Academy #3 DreamLab
 
React + Redux. Best practices
React + Redux.  Best practicesReact + Redux.  Best practices
React + Redux. Best practicesClickky
 
Battle of React State Managers in frontend applications
Battle of React State Managers in frontend applicationsBattle of React State Managers in frontend applications
Battle of React State Managers in frontend applicationsEvangelia Mitsopoulou
 
React state managmenet with Redux
React state managmenet with ReduxReact state managmenet with Redux
React state managmenet with ReduxVedran Blaženka
 
Maintaining sanity in a large redux app
Maintaining sanity in a large redux appMaintaining sanity in a large redux app
Maintaining sanity in a large redux appNitish Kumar
 
Redux training
Redux trainingRedux training
Redux trainingdasersoft
 
Redux with angular 2 - workshop 2016
Redux with angular 2 - workshop 2016Redux with angular 2 - workshop 2016
Redux with angular 2 - workshop 2016Nir Kaufman
 
React, Redux and es6/7
React, Redux and es6/7React, Redux and es6/7
React, Redux and es6/7Dongho Cho
 
Reactive.architecture.with.Angular
Reactive.architecture.with.AngularReactive.architecture.with.Angular
Reactive.architecture.with.AngularEvan Schultz
 
Integrating React.js with PHP projects
Integrating React.js with PHP projectsIntegrating React.js with PHP projects
Integrating React.js with PHP projectsIgnacio Martín
 
2018 02-22 React, Redux & Building Applications that Scale | Redux
2018 02-22 React, Redux & Building Applications that Scale | Redux2018 02-22 React, Redux & Building Applications that Scale | Redux
2018 02-22 React, Redux & Building Applications that Scale | ReduxCodifly
 
Reactивная тяга
Reactивная тягаReactивная тяга
Reactивная тягаVitebsk Miniq
 
The evolution of redux action creators
The evolution of redux action creatorsThe evolution of redux action creators
The evolution of redux action creatorsGeorge Bukhanov
 
Server side rendering with React and Symfony
Server side rendering with React and SymfonyServer side rendering with React and Symfony
Server side rendering with React and SymfonyIgnacio Martín
 
Redux for ReactJS Programmers
Redux for ReactJS ProgrammersRedux for ReactJS Programmers
Redux for ReactJS ProgrammersDavid Rodenas
 
Workshop 20: ReactJS Part II Flux Pattern & Redux
Workshop 20: ReactJS Part II Flux Pattern & ReduxWorkshop 20: ReactJS Part II Flux Pattern & Redux
Workshop 20: ReactJS Part II Flux Pattern & ReduxVisual Engineering
 
Evan Schultz - Angular Camp - ng2-redux
Evan Schultz - Angular Camp - ng2-reduxEvan Schultz - Angular Camp - ng2-redux
Evan Schultz - Angular Camp - ng2-reduxEvan Schultz
 

Similar a Ngrx slides (20)

Intro to Redux | DreamLab Academy #3
Intro to Redux | DreamLab Academy #3 Intro to Redux | DreamLab Academy #3
Intro to Redux | DreamLab Academy #3
 
Understanding redux
Understanding reduxUnderstanding redux
Understanding redux
 
React + Redux. Best practices
React + Redux.  Best practicesReact + Redux.  Best practices
React + Redux. Best practices
 
Battle of React State Managers in frontend applications
Battle of React State Managers in frontend applicationsBattle of React State Managers in frontend applications
Battle of React State Managers in frontend applications
 
React state managmenet with Redux
React state managmenet with ReduxReact state managmenet with Redux
React state managmenet with Redux
 
React lecture
React lectureReact lecture
React lecture
 
Maintaining sanity in a large redux app
Maintaining sanity in a large redux appMaintaining sanity in a large redux app
Maintaining sanity in a large redux app
 
Redux training
Redux trainingRedux training
Redux training
 
Redux with angular 2 - workshop 2016
Redux with angular 2 - workshop 2016Redux with angular 2 - workshop 2016
Redux with angular 2 - workshop 2016
 
React, Redux and es6/7
React, Redux and es6/7React, Redux and es6/7
React, Redux and es6/7
 
Reactive.architecture.with.Angular
Reactive.architecture.with.AngularReactive.architecture.with.Angular
Reactive.architecture.with.Angular
 
Integrating React.js with PHP projects
Integrating React.js with PHP projectsIntegrating React.js with PHP projects
Integrating React.js with PHP projects
 
2018 02-22 React, Redux & Building Applications that Scale | Redux
2018 02-22 React, Redux & Building Applications that Scale | Redux2018 02-22 React, Redux & Building Applications that Scale | Redux
2018 02-22 React, Redux & Building Applications that Scale | Redux
 
Reactивная тяга
Reactивная тягаReactивная тяга
Reactивная тяга
 
The evolution of redux action creators
The evolution of redux action creatorsThe evolution of redux action creators
The evolution of redux action creators
 
React redux
React reduxReact redux
React redux
 
Server side rendering with React and Symfony
Server side rendering with React and SymfonyServer side rendering with React and Symfony
Server side rendering with React and Symfony
 
Redux for ReactJS Programmers
Redux for ReactJS ProgrammersRedux for ReactJS Programmers
Redux for ReactJS Programmers
 
Workshop 20: ReactJS Part II Flux Pattern & Redux
Workshop 20: ReactJS Part II Flux Pattern & ReduxWorkshop 20: ReactJS Part II Flux Pattern & Redux
Workshop 20: ReactJS Part II Flux Pattern & Redux
 
Evan Schultz - Angular Camp - ng2-redux
Evan Schultz - Angular Camp - ng2-reduxEvan Schultz - Angular Camp - ng2-redux
Evan Schultz - Angular Camp - ng2-redux
 

Más de Christoffer Noring (20)

Azure signalR
Azure signalRAzure signalR
Azure signalR
 
Game dev 101 part 3
Game dev 101 part 3Game dev 101 part 3
Game dev 101 part 3
 
Game dev 101 part 2
Game dev 101   part 2Game dev 101   part 2
Game dev 101 part 2
 
Game dev workshop
Game dev workshopGame dev workshop
Game dev workshop
 
Deploying your static web app to the Cloud
Deploying your static web app to the CloudDeploying your static web app to the Cloud
Deploying your static web app to the Cloud
 
IaaS with ARM templates for Azure
IaaS with ARM templates for AzureIaaS with ARM templates for Azure
IaaS with ARM templates for Azure
 
Learning Svelte
Learning SvelteLearning Svelte
Learning Svelte
 
Ng spain
Ng spainNg spain
Ng spain
 
Angular Schematics
Angular SchematicsAngular Schematics
Angular Schematics
 
Design thinking
Design thinkingDesign thinking
Design thinking
 
Keynote ijs
Keynote ijsKeynote ijs
Keynote ijs
 
Vue fundamentasl with Testing and Vuex
Vue fundamentasl with Testing and VuexVue fundamentasl with Testing and Vuex
Vue fundamentasl with Testing and Vuex
 
Kendoui
KendouiKendoui
Kendoui
 
Angular mix chrisnoring
Angular mix chrisnoringAngular mix chrisnoring
Angular mix chrisnoring
 
Nativescript angular
Nativescript angularNativescript angular
Nativescript angular
 
Graphql, REST and Apollo
Graphql, REST and ApolloGraphql, REST and Apollo
Graphql, REST and Apollo
 
Angular 2 introduction
Angular 2 introductionAngular 2 introduction
Angular 2 introduction
 
Rxjs vienna
Rxjs viennaRxjs vienna
Rxjs vienna
 
Rxjs marble-testing
Rxjs marble-testingRxjs marble-testing
Rxjs marble-testing
 
Rxjs ngvikings
Rxjs ngvikingsRxjs ngvikings
Rxjs ngvikings
 

Último

Top 5 Benefits OF Using Muvi Live Paywall For Live Streams
Top 5 Benefits OF Using Muvi Live Paywall For Live StreamsTop 5 Benefits OF Using Muvi Live Paywall For Live Streams
Top 5 Benefits OF Using Muvi Live Paywall For Live StreamsRoshan Dwivedi
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Drew Madelung
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...Martijn de Jong
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)wesley chun
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfsudhanshuwaghmare1
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProduct Anonymous
 
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...DianaGray10
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...Neo4j
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Miguel Araújo
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)Gabriella Davis
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityPrincipled Technologies
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CVKhem
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024The Digital Insurer
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonAnna Loughnan Colquhoun
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024The Digital Insurer
 
Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024The Digital Insurer
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...apidays
 
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost SavingRepurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost SavingEdi Saputra
 

Último (20)

Top 5 Benefits OF Using Muvi Live Paywall For Live Streams
Top 5 Benefits OF Using Muvi Live Paywall For Live StreamsTop 5 Benefits OF Using Muvi Live Paywall For Live Streams
Top 5 Benefits OF Using Muvi Live Paywall For Live Streams
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CV
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
 
Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost SavingRepurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
 

Ngrx slides

  • 1. NGRXNGRX THERE IS A REDUCER IN MY SOUPTHERE IS A REDUCER IN MY SOUP 1
  • 2. About me Google Developer Expert Telerik Developer Expert Digital McKinsey @chris_noring 2
  • 3. NGRX IS:NGRX IS: An Angular implementation of Redux 3 . 1
  • 4. Why do we care about Redux? If you experience the following symtoms you might need Redux: A feeling that the state is spread out There are issues with updating state Some things keep changing the state but you don't know who or what Un an explainable itch 3 . 2
  • 5. Solution: a single source of truth with reducers guarding state change. Also enhanced predictability with immutable data structures 3 . 3
  • 7. Store, our data store Reducer, a function that takes state + action and produces a new state Selector, selects a slice of state Action, an intention of state change Action creator, produces an intention that may include data 3 . 5
  • 8. Typical Store content, just an object { counter: 'a value', jedis: [{ id: 1, name: 'Yoda' }], selectedJedi: { id: 1, name: 'Yoda' } } 3 . 6
  • 9. REDUCERREDUCER NEW STATE = STATE + ACTIONNEW STATE = STATE + ACTION 3 . 7
  • 10. Let's take some reducing examples: BANANA + MILK + ICE CREAM =BANANA + MILK + ICE CREAM = MILKSHAKEMILKSHAKE PIZZA + HAMBURGER + ICECREAM =PIZZA + HAMBURGER + ICECREAM = STOMACH ACHESTOMACH ACHE 3 . 8
  • 11. Mathematical function, immutable, just a calculation Immutable = Predictability //mutating var sum = 3; function add(a) { sum += a; return sum; } add(5); // 8 add(5); // 13 // immutable function computeSum(a,b) { return a + b; } computeSum(1,1); // 2 computeSum(1,1); // 2 3 . 9
  • 12. A reducer looks like the following: function reducer(state, action) { /* implementation */ } state, previous/initial state action = { type: 'my intent, e.g ADD_ITEM', payload: { /* some kind of object */ } } 3 . 10
  • 13. state + 1, instead of state +=1, immutable function counterReducer(state = 0, action) { switch(action.type) { case 'INCREMENT': return state + 1; default: return state; } } 3 . 11
  • 14. Usage let initialState = 0; let state = counterReducer(initialState,{ type: 'INCREMENT' }) // 1 state = counterReducer(state, { type: 'INCREMENT' }) // 2 function counterReducer(state = 0, action) { switch(action.type) { case 'INCREMENT': return state + 1; default: return state; } } 3 . 12
  • 15. A list reducer function jediReducer(state = [], action) { switch(action.type) { case 'ADD_JEDI': return [ ...state, action.payload]; case 'REMOVE_JEDI': return state.filter( jedi => jedi.id !== action.payload.id); default: return state; } } 3 . 13
  • 16. Usage, list reducer let state = jediReducer([], { type: 'ADD_JEDI', payload: { id: 1, name: 'Yoda' } }); // [{ id: 1, name: 'Yoda' }] state = jediReducer(state, { type: 'REMOVE_JEDI', payload: { id: 1 } }); // [] 3 . 14
  • 17. An object reducer Build a new object based on old + change with ...spread let initialState = { loading: false, data: [], error: void 0 }; function productsReducer(state = initialState, action) { switch(action.type) { case 'FETCHING_PRODUCTS': return { ...state, loading: true }; case 'FETCHED_PRODUCTS': return { ...state, data: action.payload, loading: false case 'FETCHED_PRODUCTS_ERROR': return { ...state, error: action.payload, loading: false } } 3 . 15
  • 18. Object reducer, usage Handling an AJAX case, can show spinner when, loading = true let state = productsReducer(initialState, { type: 'FETCHING_PRODUCTS' }); try { let products = await getProducts(); // from an endpoint; state = productsReducer(state, { type: 'FETCHED_PRODUCTS', payload: products }); } catch (error) { state = productsReducer(state, { type: 'FETCHED_PRODUCTS_ERROR', payload: error }); } 3 . 16
  • 19. A simple store class Store { constructor(initialState) { this.state = initialState; } dispatch(action) { this.state = calc(this.state, action); } calc(state, action) { return { counter: counterReducer(state.counter, action), jedis: jediReducer(state.jedis, action) } } } 3 . 17
  • 20. Usage, store let store = new Store({ counter: 0, jedis: [] }); store.dispatch({ type: 'INCREMENT' }); // { counter: 1, jedis: [] } store.dispatch({ type: 'ADD_JEDI', payload: { id: 1, name: 'Yoda' } }); // { counter: 1, jedis: [{ id: 1, name: 'Yoda' }] } store.dispatch({ type: 'REMOVE_JEDI', payload: { id: 1 } }); // { counter: 1, jedis: [] } 3 . 18
  • 21. Action, an object with a property 'type' and 'payload' 'type' = intent 'payload' = the change { type: 'ADD_JEDI', payload: { id: 1, name: 'Yoda' } } 3 . 19
  • 22. Action creator, function that creates action const addJedi = (id, name) => ({ type: 'ADD_JEDI', payload: { id, name } }); addJedi(1, 'Yoda'); // { type:'ADD_JEDI', payload: { id: 1, name: 'Yoda' } } //usage store.dispatch(addJedi(1, 'Yoda')); 3 . 20
  • 23. Selector, slice of state class Store { constructor(initialState) { ... } dispatch(action) { ... } calc(state, action) { ... } select(fn) { return fn(state); } } 3 . 21
  • 24. Selector, definitions const getCounter = (state) => state.counter; const getJedis = (state) => state.jedis; 3 . 22
  • 26. @ngrx/store, the store @ngrx/store-devtools, a debug tool that helps you track dispatched actions @ngrx/router-store, lets you put the routing state in the store @ngrx/effects, handles side effects @ngrx/entites, handles records @ngrx/schematics 4 . 2
  • 27. STORESTORE WHERE THE STATE LIVESWHERE THE STATE LIVES 5 . 1
  • 28. INSTALLATION AND SET UPINSTALLATION AND SET UP npm install @ngrx/store --save // file 'app.module.ts' import { StoreModule } from '@ngrx/store'; import { counterReducer } from './counter.reducer'; @NgModule({ imports: { StoreModule.forRoot({ counter: counterReducer }) } }) export class AppModule {} 5 . 2
  • 29. SHOW DATA FROM STORESHOW DATA FROM STORE // app-state.ts export interface AppState { counter: number; } // some.component.ts @Component({ template: ` {{ counter$ | async }} ` }) export class SomeComponent { counter$; constructor(this store:Store<AppState>) { this.counter$ = this.store.select('counter'); } } 5 . 3
  • 30. SHOW DATA FROM STORE, SELECTORSHOW DATA FROM STORE, SELECTOR FUNCTIONFUNCTION // app-state.ts export interface AppState { counter: number; } // some.component.ts @Component({ template: ` {{ counter$ | async }} ` }) export class SomeComponent { counter$; constructor(this store:Store<AppState>) { this.counter$ = this.store .select( state => state.counter); } } 5 . 4
  • 31. DISPATCH DATADISPATCH DATA @Component({ template: ` {{ counter$ | async }} <button (click)="increment()">Increment</button> <button (click)="decrement()">Decrement</button> ` }) export class SomeComponent { counter$; constructor(this store:Store<AppState>) { this.counter$ = this.store.select('counter'); } increment() { this.store.dispatch({ type: 'INCREMENT' }); } decrement() { this.store.dispatch({ type: 'DECREMENT' }); } } 5 . 5
  • 32. DISPATCH DATA, WITH PAYLOAD,DISPATCH DATA, WITH PAYLOAD, TEMPLATETEMPLATE @Component({ template: ` <input [(ngModel)]="newProduct" /> <div *ngFor="let product of products$ | async"> {{ product.name }} <button (click)="remove(product.id)">Remove</button> </div> <button (click)="add()">Add</button> ` }) 5 . 6
  • 33. CLASS BODYCLASS BODY export class SomeComponent { products$, id = 0; constructor(this store:Store<AppState>) { this.counter$ = this.store.select('products'); } remove(id) { this.state.dispatch({ type: 'REMOVE_PRODUCT', payload: { id } }) } add() { this.state.dispatch({ type: 'ADD_PRODUCT', payload: { id : this.id++, this.name: newProduct } }) } } 5 . 7
  • 34. When our app grows, we can't have all the state in StoreModule.forRoot({}) Solution is using StoreModule.forFeature() Let's also find a way to organize files 5 . 8
  • 35. Set up store in a feature module, StoreModule.forFeature('feature',{}) interface CombinedState { list: Product[]; item: Product; }; const combinedReducers: ActionReducerMap<CombinedState> = { list: listReducer, item: itemReducer }; @NgModule({ imports: [ StoreModule.forFeature<CombinedState, Action>( 'products', combinedReducers )] }); 5 . 9
  • 36. Organizing your files, Domain approach /feature feature.component.ts feature.selector.ts feature.actions.ts feature.reducer.ts 5 . 10
  • 37. Organizing your files, Ruby on Rails approach /feature feature.component.ts /feature2 feature2.component.ts /reducers feature.reducer.ts feature2.reducer.ts index.ts /selectors feature.selector.ts feature2.selector.ts index.ts /actions feature.actions.ts feature2.actions.ts 5 . 11
  • 38. Whatever approach you go for, consistency is key 5 . 12
  • 39. STORE DEVTOOLSSTORE DEVTOOLS DEBUG LIKE A PRODEBUG LIKE A PRO 6 . 1
  • 40. INSTALLATION AND SET UPINSTALLATION AND SET UP Install lib on NPM and download chrome extension on http://extension.remotedev.io/ 6 . 2
  • 41. npm install @ngrx/store-devtools import { StoreDevtoolsModule } from '@ngrx/store-devtools'; @NgModule({ imports: [ StoreDevtoolsModule.instrument({ maxAge: 25 // Retains last 25 states } )] }) 6 . 3
  • 42. What can we do with it? See dispatched actions Undo dispatched actions Use time travel debugging and move through time, back and forth with a gauge 6 . 4
  • 43. Initial view, we see actions as well as different tabs 6 . 5
  • 44. Here we can see the detail of an action, what type etc. 6 . 6
  • 45. It records all of our actions, here multiple dispatched actions 6 . 7
  • 46. Here we are undoing/skipping an action, store is recalculated 6 . 8
  • 47. This gauge enables us to travel through time, back and forth 6 . 9
  • 48. ROUTER STOREROUTER STORE ENABLING US TO PUT THE ROUTERENABLING US TO PUT THE ROUTER STATE IN THE STORESTATE IN THE STORE 7 . 1
  • 49. We want to accomplish the following: Save the route state to our store Customize whats gets saved down 7 . 2
  • 50. INSTALLATION AND SET UPINSTALLATION AND SET UP npm install @ngrx/router-store import { StoreRouterConnectingModule } from '@ngrx/router-stor @NgModule({ imports: [ StoreModule.forRoot({ router: routerReducer // this is where our route state g }), StoreRouterConnectingModule.forRoot({ stateKey: 'router' // name of reducer key }) ] }) 7 . 3
  • 51. We can listen to this 'router' state like any other state @Component({ template: `` }) export class SomeComponent { constructor(private state: State<AppState>) { // updates every time we route this.state.select('router') .subscribe(data => console.log(data)); } } 7 . 4
  • 52. Might be a bit hard to read 7 . 5
  • 53. LET'S BUILD OUR OWN ROUTER STATELET'S BUILD OUR OWN ROUTER STATE 7 . 6
  • 54. The following is of interest: The url The router parameters The query parameters 7 . 7
  • 55. Define a serializer, that saves url, queryParams and router params interface MyState { url: string; queryParams; params; } export class MySerializer implements RouterStateSerializer<MyState> { serialize(routerState: RouterStateSnapshot): MyState { console.log('serializer'); console.log('complete router state', routerState); const { url, root: { queryParams, firstChild: { params } } return { url, queryParams, params }; } } 7 . 8
  • 56. Provide this as the 'new' serializer @NgModule({ providers: [{ provide: RouterStateSerializer, useClass: MySerializer }] }) 7 . 9
  • 57. This is what the router state looks like now, only saves exactly what we want 7 . 10
  • 59. Objective: We want to ensure we can carry out things like accessing resources over the network. 8 . 2
  • 60. INSTALLATION AND SETUPINSTALLATION AND SETUP npm install @ngrx/effects import { EffectsModule } from '@ngrx/effects'; @NgModule({ EffectsModule.forRoot([ ... my effects classes ]) }) 8 . 3
  • 61. What kind of behaviour do we want? Set a loading flag, show spinner Do AJAX call Show fetched data or error Set loading flag to false, hide spinner 8 . 4
  • 62. NGRX approach try { store.dispatch({ type: 'FETCHING_DATA' }) // state: { loading: true } const data = await getData(); // async operation store.dispatch({ type: 'FETCHED_DATA', payload: data }); // state: { loading: false, data: {/* data from endpoint */} } catch (error) { store.dispatch({ type: 'FETCHED_DATA_ERROR', payload: error }); } 8 . 5
  • 63. My first effect @Injectable() export class ProductEffects { @Effect() products$: Observable<Action> = this.actions$.pipe( ofType(FETCHING_PRODUCTS), switchMap( // ensure we dispatch an action the last thing we do action => of({ type: "SOME_ACTION" }) ) ); constructor(private actions$: Actions<Action>, private http: console.log("product effects init"); } } 8 . 6
  • 64. My first effect - calling HTTP @Injectable() export class ProductEffects { @Effect() products$: Observable<Action> = this.actions$.pipe( ofType(FETCHING_PRODUCTS), // listen to this action switchMap(action => this.http .get("data/products.json") .pipe( delay(3000), map(fetchProductsSuccessfully), // success catchError(err => of(fetchError(err))) // error ) ) ); 8 . 7
  • 65. ENTITESENTITES REDUCE THAT BORING BOILER PLATEREDUCE THAT BORING BOILER PLATE 9 . 1
  • 66. Install and set up npm install @ngrx/entity import { EntityState, createEntityAdapter, EntityAdapter } from "@ngrx/entity"; // set up the adapter const adapter: EntityAdapter<User> = createEntityAdapter<User>(); // set up initial state const initial = adapter.getInitialState({ ids: [], entities: {} }); 9 . 2
  • 67. Install and set up /* use the adapter methods for specific cases like adapter.addOne() */ function userReducer( state = initial, action: ActionPayload<User> ) { switch (action.type) { case "ADD_USER": return adapter.addOne(action.payload, state); default: return state; } } 9 . 3
  • 68. What else can it do for us? addOne: Add one entity to the collection addMany: Add multiple entities to the collection addAll: Replace current collection with provided collection removeOne: Remove one entity from the collection removeMany: Remove multiple entities from the collection removeAll: Clear entity collection updateOne: Update one entity in the collection updateMany: Update multiple entities in the collection 9 . 4
  • 70. SCHEMATICSSCHEMATICS BE LAZY AND SCAFFOLD :)BE LAZY AND SCAFFOLD :) 10 . 1
  • 71. It is a scaffolding library that helps us scaffold out NGRX features 10 . 2
  • 72. Schematics can help us scaffold the following: Action Container Effect Entity Feature Reducer Store 10 . 3
  • 73. Install and set up // install schematics and prerequisits npm install @ngrx/schematics --save-dev npm install @ngrx/{store,effects,entity,store-devtools} --save // we might need this one as well npm install --save @angular/cli@latest 10 . 4
  • 74. Scaffold, initial state set up, forRoot({}) and instrument() ng generate store State --root --module app.module.ts --collec // result @NgModule({ declarations: [ ... ], imports: [ BrowserModule, StoreModule.forRoot(reducers, { metaReducers }), !environment.production ? StoreDevtoolsModule.instrument() ], bootstrap: [AppComponent] }) export class AppModule { } 10 . 5
  • 75. Scaffold, setting up effects for the App ng generate effect App --root --module app.module.ts --collect @NgModule({ declarations: [ AppComponent ], imports: [ ... EffectsModule.forRoot([AppEffects]) ], providers: [], bootstrap: [ ... ] }) export class AppModule { } 10 . 6
  • 76. Scaffold, create Action, generates action and test ng generate action User --spec export enum UserActionTypes { UserAction = '[User] Action' } export class User implements Action { readonly type = UserActionTypes.UserAction; } export type UserActions = User; 10 . 7
  • 77. Scaffold, create a component, with store injected ng generate container TodoList @Component({ selector: 'app-todo-list', templateUrl: './todo-list.component.html', styleUrls: ['./todo-list.component.css'] }) export class TodoListComponent implements OnInit { constructor(private store: Store<any>) { } ngOnInit() { } } 10 . 8
  • 78. DO IT YOURSELFDO IT YOURSELF BUILD YOUR OWN NGRXBUILD YOUR OWN NGRX 11 . 1
  • 79. What requirements do we have? Should be possible to dispatch actions State should update when we dispatch It should be possible to subscribe to a slice of state We should support side effects 11 . 2
  • 80. BEHAVIOURSUBJECTBEHAVIOURSUBJECT SUPPORTS A STATE, CAN BESUPPORTS A STATE, CAN BE SUBSCRIBED TOSUBSCRIBED TO 11 . 3
  • 81. BehaviorSubject // ctor value = inital value let subject = new BehaviorSubject({ value: 1 }) subject.subscribe(data => console.log('data', data)); // { value: 1 } // { prop: 2} subject.next({ prop: 2 }); 11 . 4
  • 82. Merging states with .scan() let subject = new BehaviorSubject({ value: 1 }) // {} inital subject .scan((acc, value) =>({ ...acc, ...value })) .subscribe(data => console.log('data', data)); subject.next({ prop: 2 }); // { value: 1, prop: 2 } 11 . 5
  • 83. Implementing the store class Store extends BehaviorSubject { constructor(initialState = {}) { super(initialState); this.listenerMap = {}; this.dispatcher = new Subject(); this.dispatcher .scan((acc, value) =>({ ...acc, ...value })) .subscribe(state => super.next(state)); } dispatch(newState) { this.dispatcher.next(newState); } } 11 . 6
  • 84. Usage, store store = new Store(); store.subscribe(data => console.log('state', data)); store.dispatch({ val: 1 }); store.dispatch({ prop: 'string' }); // { val: 1, prop: 'string' } 11 . 7
  • 85. What about slice of state? class Store extends BehaviorSubject { constructor(initialState = {}) { ... } dispatch(newState) { this.dispatcher.next(newState); } select(slice) { return this.map[slice] } selectWithFn(fn) { return this.map(fn) } } 11 . 8
  • 86. We need to improve the core implementation, enter storeCalc() const storeCalc = (state, action) => { return { counter: countReducer(state.counter, action), products: productsReducer(state.products, action) } }; 11 . 9
  • 87. A retake on our dispatch(), old state, getValue() + action = new state dispatch(action) { const newState = storeCalc(this.getValue(),action); this.dispatcher.next(newState); } 11 . 10
  • 88. LETS' TALK ABOUT EFFECTSLETS' TALK ABOUT EFFECTS 11 . 11
  • 89. We need to be able to signup to specific actions We need to be able to carry out side effects 11 . 12
  • 90. First let's set up subscription in the store class Store { constructor() { ... } dispatch() { ... } select() { ... } effect(listenToAction, listener) { if(!this.listenerMap.hasOwnProperty(listenToAction)) { this.listenerMap[listenToAction] = []; } this.listenerMap[listenToAction].push( listener ); } } 11 . 13
  • 91. Then ensure the effect happens in dispatch() class Store { constructor() { ... } dispatch() { const newState = storeCalc(this.getValue(),action); this.dispatcher.next(newState); // tell our listeners this action.type happened if(this.listenerMap[action.type]) { this.listenerMap[action.type].forEach(listener => { listener(this.dispatch.bind(this),action); }); } } select() { ... } effect(listenToAction, listener) { ... } } 11 . 14
  • 92. use our new effect() method let store = new Store(); store.effect('INCREMENT' ,async(dispatch, action) => { // side effect let products = await getProducts(); // side effect let data = await getData(); // dispatch, if we want dispatch({ type: 'INCREMENT' }); }) store.dispatch({ type: 'DECREMENT' }); 11 . 15
  • 94. We learned how to: Grasp the basics of Redux NGRX building blocks Use the store Leverage the dev tools and its Redux plugin Store our router state and transform it How we handle side effect like AJAX calls Remove boiler plate with Entity How to be even lazier with the scaffold tool Schematics Upgrading ourselves to Ninja level by learning how to implement NGRX 12 . 2
  • 95. Further reading: Free video course, https://platform.ultimateangular.com/courses/ngrx-st effects Redux docs, https://redux.js.org/docs Probably the best homepage on it, Brian Troncone, https://gist.github.com/btroncone/a6e4347326749f93 12 . 3
  • 96. Buy my book ( please :) ): https://www.packtpub.com/web- development/architecting-angular-applications-flux- redux-ngrx 12 . 4
  • 97. Thank you for listening 12 . 5