Ngrx
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
, inStore<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
insidengOnDestroy
. Lookup npm take until destroy. UseextractRouterParams
.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(); } }
-
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:
- How to handle runtime errors in your Angular application,
- Handling errors in NgRx Effects,
- 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.
-
Ngrx multiple actions effects:
-
Use flags in your store if you need to be notified when an action has completed. Like
{loaded: true}