Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Redux Refactoring Patterns with @ngrx/platform

Redux Refactoring Patterns with @ngrx/platform

Gregor Woiwode

June 26, 2018
Tweet

More Decks by Gregor Woiwode

Other Decks in Programming

Transcript

  1. MY FRIEND OSCAR Meet Oscar 1. My Colleage 2. He

    works with 3. Passionated about Software Development
  2. co-IT.eu GmbH We Empower Outstanding Business Solutions “ Collect your

    questions for the Q&A session, please. @GregOnNet
  3. AGENDA Our Review With Oscar 1. Property Pattern 2. @ngrx/entity

    3. @ngrx/router-store 4. selectors 5. ngrx-data
  4. export function reducer(state = initialState, action: NotesActions): State { switch

    (action.type) { case NotesActionTypes.CreateNoteSuccess: { return { ...state, notes: [...state.notes, action.payload], loaded: true, loading: false } } case NotesActionTypes.LoadNotesSuccess: { return { ...state, notes: ...action.payload, loaded: true, loading: false } /* ... */ Redundant Code
  5. export function reducer(state = initialState, action: NotesActions): State { switch

    (action.type) { case NotesActionTypes.CreateNoteSuccess: { return { ...state, notes: [...state.notes, action.payload], loaded: true, loading: false } } case NotesActionTypes.LoadNotesSuccess: { return { ...state, notes: ...action.payload, loaded: true, loading: false } /* ... */ Redundant Code
  6. /* ... */ case NotesActionTypes.LoadAdditionalNotesSuccess: { return { ...state, notes:

    [...state.notes, ...action.payload], loaded: true, loading: false } } case NotesActionTypes.DeleteSingleNoteSuccess: { return { ...state, notes: state.notes.filter(note => note.guid !== action.payload.guid), loaded: true, loading: false } } /* ... */ Remove By Filtering
  7. OSCAR‘S PROBLEM Large Switch-Case-Statements 1. Lot‘s of list operations we

    may not need every time 2. Hard to maintain 3. Less readable
  8. PROPERTY PATTERN Convert Collections To Dictionaries export interface State {

    notes: Note[]; } export interface State { notes: { [key: string]: Note }; } List to Object
  9. PROPERTY PATTERN Delete Single Object return { ...state, notes: state.notes.filter(note

    => note.guid !== action.payload.guid ) } const { [action.payload.guid]: deleted, ...withoutDeleted } = state.notes; return { ...state, notes: withoutDeleted, }
  10. MY FRIEND OSCAR Oscar asks… Maybe accessing data is more

    efficient. But the code complexity has increased. How can we benefit from this approach?
  11. @ngrx/entity Entity State export interface State extends EntityState<Note> {} export

    interface State<T> { ids: string[]; entities: { [id: string]: T }; } Leverages Property Pattern
  12. @ngrx/entity Simplify Mutations return adapter.removeOne( action.payload, state ); const {

    [action.payload.guid]: deleted, ...withoutDeleted } = state.notes; return { ...state, notes: withoutDeleted, } 1. Enusres Immutability 2. Applies State Mutation
  13. @ngrx/entity Entity Selectors const adapter = createEntityAdapter<T>(); export const {

    selectAll, selectEntities, selectIds, selectTotal } = adapter.getSelectors();
  14. @ngrx/entity Use Entity Selectors export class NotesDashboardComponent { notes$: Observable<Note[]>;

    constructor(private store: Store<fromNotes.State>) { this.notes$ = this.store.pipe(select(fromNotes.selectAll)); } }
  15. MY FRIEND OSCAR Oscar asks… @ngrx/entity is nice, thanks. Can

    we tidy up these Switch-Case-Statements now?
  16. CREATE REDUCER Factory Pattern To The Rescue const mutators: ActionHandlers<State>

    = { [NotesActionTypes.CreateNote]: notes.addOne, [NotesActionTypes.LoadNotesSuccessful]: notes.addMany }; export function reducer(state = initialState, action) { return createReducer<State>(mutators)(state, action); } Refer to last slide for the source
  17. MY FRIEND OSCAR Oscar asks… Small things can have a

    great impact to quality. But wait since you are here…
  18. OSCAR‘S PROBLEM He thinks he breakse the Redux Pattern Shouldn’t

    the store be able to answer every question of a Component?
  19. SINGLE SOURCE OF TRUTH How to deal with location state?

    /* ... */ export class NoteDetailsComponent { note$: Observable<Note>; constructor( private route: ActivatedRoute, private store: Store<fromNotes.State>) { this.note$ = this.route.params.pipe( switchMap(params => this.store.pipe(state => state.notes.entities[params.guid]) ) ); } }
  20. @ngrx/router-store Setup import { StoreRouterConnectingModule } from '@ngrx/router-store'; @NgModule({ imports:

    [ /* ... */ StoreRouterConnectingModule, ] /* ... */ }) export class AppModule {}
  21. @ngrx/router-store Types & Reducer Come For Free import * as

    fromRouter from '@ngrx/router-store'; export interface State { routerReducer: fromRouter.RouterReducerState<RouterStateUrl>; } export const reducers: ActionReducerMap<State> = { routerReducer: fromRouter.routerReducer };
  22. @ngrx/router-store Access The Router State import { createFeatureSelector } from

    '@ngrx/store'; export const getRouterState = createFeatureSelector('routerReducer');
  23. @ngrx/router-store Read Router State export const currentDetails = createSelector( getEntities,

    getRouterState, (notes, router) => entities[router.state.params.guid] ); RoutingComponent select
  24. PROPERTY PATTERN Review Store is Single Source of Truth again

    Reduced Complexity of Component Component does not rely on ActivatedRoute anymore
  25. MY FRIEND OSCAR Oscar asks… This feels more correct than

    before, thanks. Wait! There is one very last thing…
  26. OSCAR‘S PROBLEM I Can See A Revenent Pattern…. We only

    do CRUD operations. Do we need to repeat ourselves for every entity?
  27. Configure an entity export const entityMetadata: EntityMetadataMap = { Note:

    { selectId: (note: Note) => note.guid } }; export const pluralNames = { Note: 'Notes' }; ngrx-data
  28. ngrx-data Data Services import { Note } from '../../models/note'; @Injectable()

    export class NotesBoard extends EntityCollectionServiceBase<Note> { constructor(entityServiceFactory: EntityCollectionServiceFactory) { super('Note', entityServiceFactory); } }
  29. PROPERTY PATTERN Review Less Files to maintain Scales good for

    many entities Highly Configurable Handles Loading State automatically High Learning Curve Lot‘s of things happen behind the scenes
  30. CREDITS Icons - from www.flaticon.com by Vectors Market - from

    www.flaticon.com by Freepik - from www.flaticon.com by Freepik