Slide 1

Slide 1 text

co-IT.eu GmbH We Empower Outstanding Business Solutions www.co-IT.eu NgRx Refactoring Patterns @GregOnNet

Slide 2

Slide 2 text

Apple Pie

Slide 3

Slide 3 text

What is the better place to bake …?

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

NgRx is a luxurious kitchen but
 we must keep it clean.

Slide 8

Slide 8 text

@GregOnNet Speaker & Developer Gregor Woiwode

Slide 9

Slide 9 text

RECAP Redux Architecture ACTION STATE COMPONENT STORE Dispatch Send To Mutate Render Inspired by @ToddMotto REDUCERS

Slide 10

Slide 10 text

github.com/GregOnNet/keep.git

Slide 11

Slide 11 text

− Simplify Case Reducers − Automate reducer creation − Systematize state mutation operations − Isolate Store structure − Automate communication infrastructure AGENDA We will learn how to…

Slide 12

Slide 12 text

export function reducer(slice = defaults, action: NotesActions): NotesSlice { switch (action.type) { case NotesActionTypes.LoadNotesSuccess: return { !!...slice, entities: action.payload }; case NotesActionTypes.CreateNoteSuccess: return { !!...slice, entities: [!!...slice.entities, action.payload] }; case NotesActionTypes.UpdateNoteSuccess: return { !!...slice, entities: slice.entities.map(note => note.guid !!=== action.payload.guid ? action.payload : note ) REDUCERS Does this look clean to you?

Slide 13

Slide 13 text

SIMPLIFY CASE REDUCERS The Case Reducer case NotesActionTypes.LoadNotesSuccess: return { !!...slice, entities: action.payload };

Slide 14

Slide 14 text

1Set mutation logic in meaningful context.


Slide 15

Slide 15 text

SIMPLIFY CASE REDUCERS Extract Case Reducer case NotesActionTypes.LoadNotesSuccess: return setNotes(action.payload, slice); !// … function setNotes(notes: Note[], slice: NotesSlice): NotesSlice { return { ...slice, entities: notes }; }

Slide 16

Slide 16 text

Easier to read Function micro management SIMPLIFY CASE REDUCERS Review + -

Slide 17

Slide 17 text

2 Reduce code that is pure infrastructure.

Slide 18

Slide 18 text

AUTOMATE REDUCER CREATION Switch-Case-Anatomy Infrastructure UI-Logic case: a case: b case: …

Slide 19

Slide 19 text

AUTOMATE REDUCER CREATION Create Reducer Pattern createReducer Infrastructure UI-Logic Type Case Reducer Pass to

Slide 20

Slide 20 text

AUTOMATE REDUCER CREATION Create Reducer Pattern const handlers: ActionHandlers = { [NotesActionTypes.LoadNotesSuccess]: setNotes, [NotesActionTypes.UpdateNoteSuccess]: updateNote, [NotesActionTypes.CreateNoteSuccess]: createNote }; export function reducer(slice = defaults, action: NotesActions): NotesSlice { return createReducer(handlers)(slice, action); }

Slide 21

Slide 21 text

3 Reuse mutation logic.

Slide 22

Slide 22 text

Entity State export interface State extends EntityState {} export interface State { ids: string[]; entities: { [id: string]: T }; } Leverages Property Pattern SYSTEMATIZE STATE MUTATION OPERATIONS

Slide 23

Slide 23 text

Entity Adapter import { createEntityAdapter } from '@ngrx/entity'; 
 const adapter = createEntityAdapter(); The mighty helper SYSTEMATIZE STATE MUTATION OPERATIONS

Slide 24

Slide 24 text

Entity Mutators Operations for single objects and collections SYSTEMATIZE STATE MUTATION OPERATIONS

Slide 25

Slide 25 text

Simplify Mutations return adapter.removeOne( action.payload, state ); const { [action.payload.guid]: deleted, ...withoutDeleted } = state.notes; return { ...state, notes: withoutDeleted, } 1. Ensures Immutability 2. Applies State Mutation SYSTEMATIZE STATE MUTATION OPERATIONS

Slide 26

Slide 26 text

Entity Selectors const adapter = createEntityAdapter(); export const { selectAll, selectEntities, selectIds, selectTotal } = adapter.getSelectors(); SYSTEMATIZE STATE MUTATION OPERATIONS

Slide 27

Slide 27 text

Use Entity Selectors export class NotesDashboardComponent { notes$: Observable; 
 constructor(private store: Store) { this.notes$ = this.store.pipe(select(fromNotes.selectAll)); } } SYSTEMATIZE STATE MUTATION OPERATIONS

Slide 28

Slide 28 text

Less Function micro management Code is better to read Changes in Store are needed Review + + - SYSTEMATIZE STATE MUTATION OPERATIONS

Slide 29

Slide 29 text

SYSTEMATIZE STATE MUTATION OPERATIONS Changes in the Store may break your components this.notes$ = this._store.pipe( select(s => s.notes.board.entities.filter( note => new RegExp(s.search.query, 'i').test(note.title) || new RegExp(s.search.query, 'i').test(note.text) ) ) );

Slide 30

Slide 30 text

4 Isolate Store structure from Components.

Slide 31

Slide 31 text

Use Selectors import {createFeatureSelector, createSelector } from '@ngrx/store'; export const feature = createFeatureSelector('notes'); export const selector = createSelector(feature, slice => slice.); reuse ISOLATE STORE

Slide 32

Slide 32 text

Combine Selectors export const notes = createSelector(visitNotes, slice => /* */); export const search = createSelector(visitSearch, slice => /* */); export const filteredNotes = createSelector( allNotes, search, (notes, query) => /* */ ); ISOLATE STORE

Slide 33

Slide 33 text

Selectors instrument the Memoization Pattern 1 selector calculate 3 selector recalculate Store change event triggered 2 selector cache ISOLATE STORE

Slide 34

Slide 34 text

Reduces complexity of components Changes in Store are transparent More decentralized code Review + + - SYSTEMATIZE STATE MUTATION OPERATIONS

Slide 35

Slide 35 text

5 Automate communication Infrastructure

Slide 36

Slide 36 text

NgRx Ducks

Slide 37

Slide 37 text

− Create Action Types − Create Action Creators − Enhance reducer functions − Remember Selector-, Action Type- & Action Creator import paths Stop repetition of the same tasks NGRX DUCKS

Slide 38

Slide 38 text

npm install @co-it/ngrx-ducks Hosted at

Slide 39

Slide 39 text

@Component({ … }) export class NotesListComponent { notes$: Observable; constructor(@Inject(NoteDucks) private _ducks: Ducks) { this.notes$ = this._ducks.pick(fromNotes.filtered); } addToCollection(draft: NoteDraft) { this._ducks.createNote.dispatch(draft); } } NGRX DUCKS A Duck is a Service

Slide 40

Slide 40 text

NGRX DUCKS A Duck are strictly typed

Slide 41

Slide 41 text

@Injectable() export class NotesEffects { @Effect() loadNotes$ = this.actions$.pipe( ofType(this._ducks.loadAll.type), exhaustMap(() => this._notes.all()), map(notes => this._ducks.set.plain(notes)) ); } NGRX DUCKS A Duck is a Action Creator

Slide 42

Slide 42 text

export class NoteDucks { loadAll = effect('[Notes] Load Notes'); // … set(slice: NotesSlice, notes: Note[]) { return adapter.addAll(notes, slice); } create(slice: NotesSlice, note: Note) { return adapter.addOne(note, slice); } // … } NGRX DUCKS A Duck bundles mutation logic

Slide 43

Slide 43 text

import { createReducerFrom, effect, wireUpActions } from '@co-it/ngrx-ducks'; export const wiredActions = wireUpActions(NoteDucks, { set: '[Notes/API] Loading Notes succeeded', create: '[Notes/API] Creating Note succeeded', update: '[Notes/API] Updating Note succeeded' }); NGRX DUCKS A Duck connect mutators with action types

Slide 44

Slide 44 text

Review SYSTEMATIZE STATE MUTATION OPERATIONS Read & Write API Single Place to manage actions IoC Support No schematics yet + + + -

Slide 45

Slide 45 text

co-IT.eu GmbH We Empower Outstanding Business Solutions Thank you! @GregOnNet github.com/GregOnNet/keep.git

Slide 46

Slide 46 text

Special thanks go to… Sascha Nuissl @saschanuissl Creator of the NgRx Ducks Logo ♥

Slide 47

Slide 47 text

co-IT.eu GmbH We Empower Outstanding Business Solutions www.co-IT.eu NgRx Refactoring Patterns @GregOnNet