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

NgRx Refactoring Patterns

NgRx Refactoring Patterns

NgRx helps you with centralized state management in Angular.
But what helps to keep your code clean an maintainable as your project grows?

You can choose from various built-in features that NgRx already provides.
But there is even more to discover!

After this talk, you will know about the following patterns and frameworks helping you to scale your NgRx application.

- Property Pattern
- createReducer Factory
- @ngrx/entity
- @ngrx/router-store
- ngrx-data
- ngrx-ducks

Gregor Woiwode

November 30, 2018
Tweet

More Decks by Gregor Woiwode

Other Decks in Programming

Transcript

  1. RECAP Redux Architecture ACTION STATE COMPONENT STORE Dispatch Send To

    Mutate Render Inspired by @ToddMotto REDUCERS
  2. − Simplify Case Reducers − Automate reducer creation − Systematize

    state mutation operations − Isolate Store structure − Automate communication infrastructure AGENDA We will learn how to…
  3. 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?
  4. 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 }; }
  5. AUTOMATE REDUCER CREATION Create Reducer Pattern const handlers: ActionHandlers<NotesSlice> =

    { [NotesActionTypes.LoadNotesSuccess]: setNotes, [NotesActionTypes.UpdateNoteSuccess]: updateNote, [NotesActionTypes.CreateNoteSuccess]: createNote }; export function reducer(slice = defaults, action: NotesActions): NotesSlice { return createReducer(handlers)(slice, action); }
  6. Entity State export interface State extends EntityState<Note> {} export interface

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

    adapter = createEntityAdapter<T>(); The mighty helper SYSTEMATIZE STATE MUTATION OPERATIONS
  8. 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
  9. Entity Selectors const adapter = createEntityAdapter<T>(); export const { selectAll,

    selectEntities, selectIds, selectTotal } = adapter.getSelectors(); SYSTEMATIZE STATE MUTATION OPERATIONS
  10. Use Entity Selectors export class NotesDashboardComponent { notes$: Observable<Note[]>; 


    constructor(private store: Store<fromNotes.State>) { this.notes$ = this.store.pipe(select(fromNotes.selectAll)); } } SYSTEMATIZE STATE MUTATION OPERATIONS
  11. Less Function micro management Code is better to read Changes

    in Store are needed Review + + - SYSTEMATIZE STATE MUTATION OPERATIONS
  12. 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) ) ) );
  13. Use Selectors import {createFeatureSelector, createSelector } from '@ngrx/store'; export const

    feature = createFeatureSelector<HarbourSlice>('notes'); export const selector = createSelector(feature, slice => slice.<property>); reuse ISOLATE STORE
  14. Combine Selectors export const notes = createSelector(visitNotes, slice => /*

    */); export const search = createSelector(visitSearch, slice => /* */); export const filteredNotes = createSelector( allNotes, search, (notes, query) => /* */ ); ISOLATE STORE
  15. Selectors instrument the Memoization Pattern 1 selector calculate 3 selector

    recalculate Store change event triggered 2 selector cache ISOLATE STORE
  16. Reduces complexity of components Changes in Store are transparent More

    decentralized code Review + + - SYSTEMATIZE STATE MUTATION OPERATIONS
  17. − 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
  18. @Component({ … }) export class NotesListComponent { notes$: Observable<Note[]>; constructor(@Inject(NoteDucks)

    private _ducks: Ducks<NoteDucks>) { this.notes$ = this._ducks.pick(fromNotes.filtered); } addToCollection(draft: NoteDraft) { this._ducks.createNote.dispatch(draft); } } NGRX DUCKS A Duck is a Service
  19. @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
  20. 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
  21. 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
  22. Review SYSTEMATIZE STATE MUTATION OPERATIONS Read & Write API Single

    Place to manage actions IoC Support No schematics yet + + + -