Common pitfalls and solutions

I’m working on an example Angular application that showcases some common pitfalls and solutions when using NgRx.

Angular Connect 2018 & 2017 videos

Here’s a link to the videos from last year’s conference. There were quite a few talks this year as well as useful slots where you could meet other people. I spent my time conversing with interesting fellows, but I did see some talks. Here’s a list of the ones I did see and appreciate:

NgRx Workshop

I also attended a workshop on NgRx. Whats follows below is a bullet point list that summarises the important content of the workshop.

  • There is a new website for NgRX, take a look at their new guide.

  • Always pass the root state type, e.g. ApplicationState, in Store<ApplicationState> in the constructor for injection
    // src/store/root.state.ts
    export interface State {
      firstName: string;
      lastName: string;
    }
    
    import { ApplicationStoreState } from '../../store';
    @Component({
      selector: 'app-my-comp',
      templateUrl: './my.component.html'
    })
    class MyComponent {
      time$: Observable<number>;
    
      constructor(private store: Store<ApplicationStoreState.State>) {
          this.time$ = store.pipe(select(ApplicationQuery.getTime))l
      }
    }
    
  • Always derive from the root state, e.g. ApplicationState inside a selector query:
    // src/store/root.state.ts
    export interface ApplicationState {
      contacts: ContactState
    }
    
    // store/contacts/contacts.query.ts
    export const (state: ApplicationState) => state.contacts.list
    
  • Use namespaces for queries. This creates a single point for reusable query functions which can be shared and easily tested.
    export namespace ContactsQuery {
      export const getContacts = (state: ApplicationState) => state.contacts.list
    }
    
  • Use utility functions as sub-reducers inside your reducers to break down complexity
    export function reducer(state: State, action: Action) {
      switch(action.type) {
          case ACTION_1:
          case ACTION_2:
              return subReducer1And2(state, action)
          case ACTION_3: 
              return subReducer3(state, action)
    
          case default:
              return state
      }
    }
    
  • Use facade pattern to hide the store in the constructor, basically behind a service The components only have access to the exposing functions via the facade
    export class MyComponentFacade {
      constructor(private service1: FirstService, private service2: SecondService) { }
    
      public getFromFirstAndSecond() {
          return this.service1.getFirstValue() + this.service2.getSecondValue();
      }
    }
    
  • Return the same reference to unchanged objects while inside a reducer See page 62 in the slides, always return the same reference to items that have not changed

  • Use store-freeze during development to ensure that you are not accidentally breaking the NgRx contract.

  • Router state ngrx-router-store router-store

  • For subscriptions of RxJS observables, use takeUntil inside ngOnDestroy. Lookup npm take until destroy. Use extractRouterParams.
    class Component {
      ngUnsubscribe$ = new BehaviourSubject();
    
      constructor() {
    
      //
          someSub = this.store.pipe(
              select(thing), 
              takeUntil(this.ngUnsubscribe)
          ).subscribe(x => {
              // Get things until subscribe
          });
      }
    
      ngOnDestroy() {
          this.ngUnsubscribe.next();
      }
    }
    

    Read more about subscriptions here.

  • Only store state that needs to be persisted between application sessions.

  • Marble tests are useful for unit testing Observables. See this article for more information.

  • How do you unit test a component that uses NgRx? Mock the store behind a facade in the component, unit test the reducer and the selectors separately

  • JSBin is useful for making public small examples for JavaScript. See this link to jsbin.com.

  • Error reporting & handling:
    1. How to handle runtime errors in your Angular application,
    2. Handling errors in NgRx Effects,
    3. Global error handling with Angular and NgRx, Basically, capture errors inside @Effects, for side-effects like when you’re talking to a REST endpoint, and use that to trigger a toast.
  • Use flags in your store if you need to be notified when an action has completed. Like {loaded: true}

  • How to use effects