myHotTake

Tag: Fix NgRx state issues

  • Debugging NgRx Effects, Reducers, and Actions Explained

    If this analogy clicks for you, hit that like button or share it with someone diving into NgRx debugging!


    Always think of debugging NgRx state like running quality checks in a giant shipping warehouse. Stay with me. I’m managing this warehouse where every package (the state) has a label that says exactly where it needs to go. The workers in my warehouse are the reducers, responsible for organizing and labeling these packages when they arrive. The delivery drivers? They’re my effects, taking packages and ensuring they reach their final destination outside the warehouse.

    One day, I realize some packages are ending up in the wrong section, or worse—just piling up without going anywhere. It’s chaos. So, I grab my clipboard (my debugging tools) and start investigating.

    First, I double-check the workers. Are the reducers putting the right labels on each package? I inspect each worker’s instructions (my reducer logic) to see if there’s a typo or a missing step. If the labels are wrong, I know the problem is upstream.

    Next, I look at the conveyor belts—the actions flowing through the system. Are the right packages being sent to the right workers? This is where tools like NgRx DevTools shine, helping me trace the journey of every package, from the time it’s created (dispatched) to where it ends up (state changes). It’s like replaying security footage to see where things went off-track.

    If the packages are labeled correctly but still aren’t reaching their destinations, I investigate the drivers. Are the effects picking up the right packages and delivering them properly? Maybe one driver’s GPS is broken (an HTTP call failed) or they missed a route entirely (an effect isn’t dispatching an action).

    By methodically following the packages—state changes—through the warehouse, I can pinpoint exactly where the issue lies. It’s a systematic process, and with tools like DevTools, it feels like having a barcode scanner to track every single package.

    Debugging NgRx is like this: follow the flow, check each station, and trust the tools to guide you. And when it all runs smoothly again? It’s like watching a perfectly organized warehouse in action.


    1️⃣ Checking the Workers (Reducers)

    In our warehouse, workers mislabeling packages is equivalent to reducers incorrectly updating the state. Say we have a state that tracks a list of orders:

    export interface OrderState {
      orders: Order[];
      error: string | null;
    }
    
    const initialState: OrderState = {
      orders: [],
      error: null,
    };
    
    export const orderReducer = createReducer(
      initialState,
      on(loadOrdersSuccess, (state, { orders }) => ({
        ...state,
        orders: orders, // Correct labeling
        error: null,
      })),
      on(loadOrdersFailure, (state, { error }) => ({
        ...state,
        error: error, // Logging the problem
      }))
    );

    If orders aren’t updating correctly, I’ll first inspect the actions dispatched. Using the Redux DevTools, I can verify if the loadOrdersSuccess action carries the right payload (orders). If not, the issue might be with the action or the API call that fetched this data.


    2️⃣ Following the Conveyor Belts (Actions)

    Actions represent the flow of packages through the warehouse. If they’re not reaching the workers, nothing changes in the state.

    export const loadOrders = createAction('[Order] Load Orders');
    export const loadOrdersSuccess = createAction(
      '[Order] Load Orders Success',
      props<{ orders: Order[] }>()
    );
    export const loadOrdersFailure = createAction(
      '[Order] Load Orders Failure',
      props<{ error: string }>()
    );

    Using the DevTools, I’d ensure:

    1. The loadOrders action was dispatched.
    2. It triggered the expected loadOrdersSuccess or loadOrdersFailure.

    If I don’t see the success or failure action, it might be a problem with the effect.


    3️⃣ Inspecting the Drivers (Effects)

    Effects are responsible for calling APIs or performing side effects. A buggy driver might fail to deliver a package to its destination.

    @Injectable()
    export class OrderEffects {
      loadOrders$ = createEffect(() =>
        this.actions$.pipe(
          ofType(loadOrders),
          mergeMap(() =>
            this.orderService.getOrders().pipe(
              map((orders) => loadOrdersSuccess({ orders })),
              catchError((error) => of(loadOrdersFailure({ error })))
            )
          )
        )
      );
    
      constructor(
        private actions$: Actions,
        private orderService: OrderService
      ) {}
    }

    If an effect isn’t dispatching loadOrdersSuccess or loadOrdersFailure, I’d:

    • Confirm that this.orderService.getOrders() is returning the expected data.
    • Use console logs or a debugger inside the effect to trace its execution.
    • Ensure the action type in ofType(loadOrders) matches exactly.

    Key Takeaways

    • Break it down: Debugging NgRx state is about following the flow—actions trigger effects, effects update state via reducers.
    • Use tools: Redux DevTools is like a barcode scanner, letting you trace actions and state changes in real time.
    • Be methodical: Inspect reducers, actions, and effects step by step.
    • Add safety nets: Use catchError in effects and maintain a clear error state in reducers to capture and debug failures.

    When I debug NgRx issues, I feel like the warehouse manager walking through each station with a checklist. By focusing on one part of the system at a time, I can pinpoint the issue and get everything back on track efficiently.