myHotTake

Tag: NgRx best practices

  • Avoiding Common NgRx Mistakes: A Chef’s Guide to Success

    If you find this story helpful, feel free to like or share it with others who might benefit too!


    I’m a chef in a kitchen, and my task is to prepare a complex dish using NgRx as my recipe guide. NgRx is like a sophisticated cookbook that helps me manage the chaos of orders and ingredients flowing through the kitchen.

    One common mistake I might make as a chef is trying to cook every dish myself, similar to how I might mistakenly handle too many side effects directly within my components instead of using effects for managing those side effects. It’s like attempting to juggle all the pans at once, rather than letting my sous-chefs (effects) handle specific tasks like fetching ingredients (API calls) or notifying the waitstaff (updating the UI).

    Another pitfall in my culinary journey is not organizing my pantry, which mirrors how I might mismanage the state structure. If I don’t categorize my ingredients (state) properly, I end up wasting time searching for what I need. In NgRx, I should ensure my state is well-organized and normalized, so I can quickly access the data, just like having a neat pantry where everything has its place.

    Then there’s the temptation to overcomplicate the recipe. I might add too many garnishes or unnecessary steps, akin to over-engineering selectors or actions in NgRx. Instead, I should keep things simple and focused, ensuring each action and selector has a clear purpose, just like each ingredient should enhance the dish without overwhelming it.

    Lastly, I might forget to taste as I go, which is equivalent to neglecting to test my NgRx setup. I need to continually sample the dish (test my code) to ensure it’s coming together as expected, allowing me to catch any mistakes early rather than after it’s served.

    NgRx, much like a great recipe, requires balance, organization, and constant attention to detail. By avoiding these common mistakes, I can create a seamless and efficient kitchen, where every dish is a success.


    Continuing my role as a chef, I realize that each section of the kitchen (or each part of my NgRx setup) has a specific function. Let’s explore how I can translate my cooking experience into code.

    1. Delegating Tasks to Sous-Chefs (Effects): In the kitchen, I delegate tasks to my sous-chefs. Similarly, in NgRx, I use effects to handle side effects. Here’s a code snippet:
       import { Injectable } from '@angular/core';
       import { Actions, createEffect, ofType } from '@ngrx/effects';
       import { map, mergeMap } from 'rxjs/operators';
       import { MyService } from './my-service';
       import * as MyActions from './my.actions';
    
       @Injectable()
       export class MyEffects {
    
         constructor(
           private actions$: Actions,
           private myService: MyService
         ) {}
    
         loadItems$ = createEffect(() => this.actions$.pipe(
           ofType(MyActions.loadItems),
           mergeMap(() => this.myService.getItems()
             .pipe(
               map(items => MyActions.loadItemsSuccess({ items }))
             ))
         ));
       }

    Here, the effect loadItems$ listens for loadItems actions and delegates the task of fetching items to the myService.

    1. Organizing the Pantry (State Structure): Just as I organize my pantry, I need to structure my state clearly. Here’s an example of a well-structured state:
       export interface AppState {
         products: ProductState;
         users: UserState;
       }
    
       export interface ProductState {
         items: Product[];
         loading: boolean;
       }
    
       export interface UserState {
         profile: UserProfile;
         isLoggedIn: boolean;
       }

    This structure allows me to easily access and manage different parts of the state, just like finding ingredients quickly in a well-organized pantry.

    1. Keeping the Recipe Simple (Selectors and Actions): I avoid overcomplicating my dishes by keeping my actions and selectors simple:
       // Actions
       export const loadItems = createAction('[Product] Load Items');
       export const loadItemsSuccess = createAction('[Product] Load Items Success', props<{ items: Product[] }>());
    
       // Selectors
       import { createSelector, createFeatureSelector } from '@ngrx/store';
    
       export const selectProductState = createFeatureSelector<ProductState>('products');
    
       export const selectAllItems = createSelector(
         selectProductState,
         (state: ProductState) => state.items
       );

    Each action and selector has a clear and specific purpose, much like each ingredient in a dish.

    1. Tasting as I Cook (Testing): Finally, I ensure everything tastes right by testing my NgRx setup:
       it('should load items successfully', () => {
         const { result } = reducer(initialState, loadItemsSuccess({ items: mockItems }));
         expect(result.items).toEqual(mockItems);
       });

    This test ensures that my actions and reducers work as expected, just like tasting my dish to avoid surprises.

    Key Takeaways:

    • Use Effects Wisely: Delegate side effects to effects to keep components clean and focused.
    • Organize State Structure: A well-structured state is like an organized pantry, making data access efficient.
    • Simplicity is Key: Keep actions and selectors straightforward to avoid over-engineering.
    • Test Regularly: Testing ensures your NgRx setup works as intended, similar to tasting a dish throughout its preparation.