Save 37% off PRO during our Black Friday Sale! »

Redux Refactoring Patterns with @ngrx/platform

Redux Refactoring Patterns with @ngrx/platform

Ce02d91f9c2030bea48ed38376b95771?s=128

Gregor Woiwode

June 26, 2018
Tweet

Transcript

  1. Redux Refactoring Patterns @GregOnNet co-IT.eu GmbH We Empower Outstanding Business

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

    works with 3. Passionated about Software Development
  3. MY FRIEND OSCAR Meet Oscar 1. …He is not an

    early morning person.
  4. Your Code Should Always Feel Like Home.

  5. @GregOnNet Gregor Woiwode CTO

  6. co-IT.eu GmbH We Empower Outstanding Business Solutions “ Collect your

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

    3. @ngrx/router-store 4. selectors 5. ngrx-data
  8. 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
  9. 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
  10. /* ... */ 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
  11. 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
  12. Property Pattern SIMPLIFY STATE MUTATIONS

  13. PROPERTY PATTERN Convert Collections To Dictionaries export interface State {

    notes: Note[]; } export interface State { notes: { [key: string]: Note }; } List to Object
  14. 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, }
  15. MY FRIEND OSCAR Oscar asks… Maybe accessing data is more

    efficient. But the code complexity has increased. How can we benefit from this approach?
  16. PROPERTY PATTERN Review Less Collection Operations More Performant Operations Less

    Readable Code
  17. @ngrx/entity SIMPLIFY REDUCERS

  18. @ngrx/entity Entity State export interface State extends EntityState<Note> {} export

    interface State<T> { ids: string[]; entities: { [id: string]: T }; } Leverages Property Pattern
  19. @ngrx/entity Entity Adapter import { createEntityAdapter } from '@ngrx/entity'; const

    adapter = createEntityAdapter<T>(); The mighty helper
  20. @ngrx/entity Entity Mutators Operations for single objects and collections

  21. @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
  22. @ngrx/entity Entity Selectors const adapter = createEntityAdapter<T>(); export const {

    selectAll, selectEntities, selectIds, selectTotal } = adapter.getSelectors();
  23. @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)); } }
  24. Demo ENTITY ADAPTER

  25. PROPERTY PATTERN Review Less Code Duplication Reusable Mutators & Selectors

  26. MY FRIEND OSCAR Oscar asks… @ngrx/entity is nice, thanks. Can

    we tidy up these Switch-Case-Statements now?
  27. createReducer SIMPLIFY REDUCER INFRASTRUCTURE

  28. CREATE REDUCER Switch-Case-Anatomy Infrastructure UI-Logic case: a case: b case:

  29. 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
  30. Demo REMOVE SWITCH-CASE-STATEMENTS

  31. PROPERTY PATTERN Review Reduced Infrastructure Code Reducers do not need

    to be enhanced as Requirements change
  32. MY FRIEND OSCAR Oscar asks… Small things can have a

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

    the store be able to answer every question of a Component?
  34. 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]) ) ); } }
  35. @ngrx/router-store SIMPLIFY LOCATION STATE HANDLING

  36. @ngrx/router-store Setup import { StoreRouterConnectingModule } from '@ngrx/router-store'; @NgModule({ imports:

    [ /* ... */ StoreRouterConnectingModule, ] /* ... */ }) export class AppModule {}
  37. @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 };
  38. @ngrx/router-store Enhance The State Tree Store Notes Router State provided

    by @ngrx/router-store
  39. @ngrx/router-store Access The Router State import { createFeatureSelector } from

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

    getRouterState, (notes, router) => entities[router.state.params.guid] ); RoutingComponent select
  41. @ngrx/router-store The Reducer‘s Outcome Let’s traverse through the whole state

    tree! Of Cause Not!
  42. Demo COMPRESS ROUTER STATE

  43. PROPERTY PATTERN Review Store is Single Source of Truth again

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

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

    do CRUD operations. Do we need to repeat ourselves for every entity?
  46. “ I see reccurring patterns…

  47. Looking for the right answer…

  48. ngrx-data SIMPLIFY STORE USING CONVENTIONS

  49. Know, Can Do… Put Your Know-How Into Action!

  50. ngrx-data Overview Actions Reducers Effects Data Services Dynamically sets up…

  51. Configure an entity export const entityMetadata: EntityMetadataMap = { Note:

    { selectId: (note: Note) => note.guid } }; export const pluralNames = { Note: 'Notes' }; ngrx-data
  52. Setup Entity Cache ngrx-data NgrxDataModule.forRoot({ entityMetadata, pluralNames }),

  53. ngrx-data Data Services import { Note } from '../../models/note'; @Injectable()

    export class NotesBoard extends EntityCollectionServiceBase<Note> { constructor(entityServiceFactory: EntityCollectionServiceFactory) { super('Note', entityServiceFactory); } }
  54. Convention Based Data Services ngrx-data localhost:4200/api/notes/<id> root entityName parameters

  55. ngrx-data Built-In Functions Of A Data Service Mutators & Selectors

    for the given entity.
  56. Demo CONFIGURE NGRX-DATA

  57. ngrx-data Get Rid Of Unneeded Files Delete Delete Custom Configuration

  58. 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
  59. co-IT.eu GmbH We Empower Outstanding Business Solutions Thank you! @GregOnNet

    e.co-it.eu/dwx18-gift e.co-it.eu/dwx18-ng
  60. CREDITS Icons - from www.flaticon.com by Vectors Market - from

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