Slide 1

Slide 1 text

Angular at Scale …with Redux powered by NgRx

Slide 2

Slide 2 text

Gregor Woiwode

Slide 3

Slide 3 text

Gregor Woiwode co-IT.eu GmbH @GregOnNet

Slide 4

Slide 4 text

Kentan NgRx Ducks Schematics

Slide 5

Slide 5 text

@GregOnNet Topics Common Pitfalls Why, How & What of State Management Redux Architecture NgRx platform

Slide 6

Slide 6 text

function iDoToMuch(input: any) { if (input.hasPropertyA) { !/* !!... !*/ if (input.hasAlsoPropertyA2) { !/* !!... !*/ } else if (input.hasAlsoPropertyA3) { input.propertyList.map(property !=> { !/* !!... !*/ }); } } if (input.hasPropertyB) { !/* !!... !*/ } input = updateInput(input); return input.fullFillsCondition ? changeInputTheOneWay(input) : changeInputTheOtherWay(input); } Nested Code Blocks Cyclometic Complexity of 10 Tests??? Complexity @GregOnNet

Slide 7

Slide 7 text

@GregOnNet

Slide 8

Slide 8 text

Modal Wrapper Monoliths Modal Stepper Questionnaire Button Group @GregOnNet

Slide 9

Slide 9 text

Modal Wrapper Monoliths Modal Stepper Questionnaire Button Group No clear API Deeply nested Components Hard for others to understand Hard to maintain @GregOnNet

Slide 10

Slide 10 text

Monoliths Reduce Component Nesting Think in APIs Put a smile on your colleagues’ face @GregOnNet

Slide 11

Slide 11 text

Learnings Applying valuable patterns is hard. It is nearly impossible to know the whole Project you are working on. It is often ignored that requirements change over time. @GregOnNet

Slide 12

Slide 12 text

Decentralised state @GregOnNet State is spread across multiple components.

Slide 13

Slide 13 text

@GregOnNet Distant leaf components need access to state. Decentralised state

Slide 14

Slide 14 text

Centralised state Interact with single source @GregOnNet

Slide 15

Slide 15 text

Store Redux Component Action Reducers @GregOnNet Dispatch Processed Mutate Read

Slide 16

Slide 16 text

Redux Component Action Reducers @GregOnNet Read Write

Slide 17

Slide 17 text

Redux implements Command-Query-Segregation. @GregOnNet

Slide 18

Slide 18 text

Redux separates read- and write logic. @GregOnNet

Slide 19

Slide 19 text

Redux allows writing smaller modules. @GregOnNet

Slide 20

Slide 20 text

Redux decentralises your code. @GregOnNet

Slide 21

Slide 21 text

@GregOnNet interface Action { type: string } Action

Slide 22

Slide 22 text

@GregOnNet interface Action { type: string } Action type needs to be unique Represents an interaction with UI or Data Takes an optional payload or metadata

Slide 23

Slide 23 text

@GregOnNet Action interface Action { type: string payload: T }

Slide 24

Slide 24 text

Reducer Reducer A Reducer B Reduce @GregOnNet A reducer is responsible for processing actions

Slide 25

Slide 25 text

Reducer Reducer A Reducer B Reduce @GregOnNet

Slide 26

Slide 26 text

Reducer Reducer B @GregOnNet Function

Slide 27

Slide 27 text

Store Principles @GregOnNet Read-Only Single Source of Truth Mutations are made by pure functions

Slide 28

Slide 28 text

Pure function @GregOnNet function iAmPure(input: string): string { return `${input}1`; }

Slide 29

Slide 29 text

Impure function @GregOnNet function iAmImpurePure(): string { const input = document.querySelector('input'); return `${input.value}1`; }

Slide 30

Slide 30 text

Store-Slices @GregOnNet Reducer B Reducer A Reducer C

Slide 31

Slide 31 text

Store @GregOnNet Reducer B Reducer A Reducer C const state = { featureA: { !/* !!... !*/ }, featureB: { !/* !!... !*/ }, featureC: { !/* !!... !*/ } } Store is an In-Memory data structure

Slide 32

Slide 32 text

@GregOnNet NgRx https://ngrx.io/

Slide 33

Slide 33 text

@GregOnNet @NgModule({ imports: [ StoreModule.forRoot(reducers, { }) !/* !!... !*/ }) export class AppModule { } StoreModule

Slide 34

Slide 34 text

@GregOnNet @NgModule({ imports: [ StoreModule.forRoot(reducers, { runtimeChecks: { } }) !/* !!... !*/ }) export class AppModule { } StoreModule

Slide 35

Slide 35 text

@GregOnNet StoreModule @NgModule({ imports: [ StoreModule.forRoot(reducers, { runtimeChecks: { strictActionImmutability: true, strictActionSerializability: true, strictStateImmutability: true, strictStateSerializability: true } }) !/* !!... !*/ }) export class AppModule { }

Slide 36

Slide 36 text

@GregOnNet StoreModule - reducers export const reducers: ActionReducerMap = { counter: counterReducer, logger: loggerReducer } Reducers get combined feature name

Slide 37

Slide 37 text

@GregOnNet createAction export const increment = createAction( '[Counter] Increment by one' ); !// createAction(); !// !// { !// type: '[Counter] Increment by one' !// }

Slide 38

Slide 38 text

@GregOnNet createAction & payload export const add = createAction( '[Counter] Add value', props<{ payload: { value: number } }>() ); !// createAction({ payload: { value: 1 } }) !// !// { !// type:'[Counter] Add value', !// payload: { value: 1} !// }

Slide 39

Slide 39 text

@GregOnNet Read from the store export class SimpleCounterComponent { count$: Observable; constructor(private store: Store) { this.count$ = this.store.pipe( select(state !=> state.counter.count) ); } }

Slide 40

Slide 40 text

@GregOnNet DEMO

Slide 41

Slide 41 text

Redux - Inter app communication Component Action Reducers @GregOnNet Dispatch Processed Mutate Read

Slide 42

Slide 42 text

Redux - Side effects Component Action Reducers @GregOnNet Dispatch Action Effects Dispatch

Slide 43

Slide 43 text

@GregOnNet @NgModule({ imports: [ !/* !!... !*/, EffectsModule.forRoot([CounterEffects]) }) export class AppModule { } EffectsModule

Slide 44

Slide 44 text

Redux - Side effects @GregOnNet @Injectable() export class CounterEffects { randomAdd = createEffect(() !=> this.actions.pipe( ofType(randomAdd) )); constructor(private actions: Actions) { } }

Slide 45

Slide 45 text

Redux - Side effects @GregOnNet @Injectable() export class CounterEffects { randomAdd = createEffect(() !=> this.actions.pipe( ofType(randomAdd), switchMap((action !=> !/* e.g. call API !*/) )); constructor(private actions: Actions) { } }

Slide 46

Slide 46 text

Redux - Side effects @GregOnNet @Injectable() export class CounterEffects { randomAdd = createEffect(() !=> this.actions.pipe( ofType(randomAdd), switchMap((action !=> !/* e.g. call API !*/), map((result !=> randomAddSuccess(result)) )); constructor(private actions: Actions) { } }

Slide 47

Slide 47 text

Redux - Side effects @GregOnNet @Injectable() export class CounterEffects { randomAdd = createEffect(() !=> this.actions.pipe( ofType(randomAdd), switchMap((action !=> !/* e.g. call API !*/), ), { dispatch: true|false }); constructor(private actions: Actions) { } }

Slide 48

Slide 48 text

Redux - Side effects @GregOnNet @Injectable() export class CounterEffects { randomAdd = createEffect(() !=> this.actions.pipe( ofType(randomAdd), switchMap((action !=> !/* call API !*/), map((result !=> randomAddSuccess(result)) ), { resubscribeOnError: true|false }); constructor(private actions: Actions) { } }

Slide 49

Slide 49 text

@GregOnNet DEMO

Slide 50

Slide 50 text

Selectors @GregOnNet this.store.pipe( select(state !=> state.counter.count) ); Inline selectors

Slide 51

Slide 51 text

Selectors @GregOnNet this.store.pipe( select(state !=> state.cou ); Break quickly when Store changes Not reusable

Slide 52

Slide 52 text

Selectors @GregOnNet const featureCounter = createFeatureSelector('counter'); feature name Jump to a feature

Slide 53

Slide 53 text

Selectors @GregOnNet export const count = createSelector(featureCounter, s !=> s.count); Project data const featureCounter = createFeatureSelector('counter');

Slide 54

Slide 54 text

Selectors @GregOnNet Combine selectors aggregating data export const infoWithCurrentCount = createSelector( infoMessage, count, (messages, total) !=> messages.map(message !=> `${message} (Total: ${total})` ) );

Slide 55

Slide 55 text

@GregOnNet Selectors simplify Store changes.

Slide 56

Slide 56 text

@GregOnNet are composable. Selectors

Slide 57

Slide 57 text

@GregOnNet use memoization. Selectors

Slide 58

Slide 58 text

@GregOnNet Selectors Memoization 1 selector calculate 2 selector use cache 3 selector calculate

Slide 59

Slide 59 text

@GregOnNet DEMO

Slide 60

Slide 60 text

What we have covered… Redux Architecture Actions & Reducers StoreDevtools Effects Selectors @ngrx/entity @ngrx/router-store @ngrx/data

Slide 61

Slide 61 text

Closing thoughts On-Board your team patiently. Modeling application state has lots of overlaps
 with Requirements Engineering. Redux makes your App more predictive.

Slide 62

Slide 62 text

Gregor Woiwode Thank you for having me! @GregOnNet https://bit.do/ng-scale-chemnitz https://stackblitz.com/edit/ngrx-8-playground