myHotTake

Tag: Angular state effects

  • Why Use NgRx Effects for State Management?

    Love clever analogies that make tech simple? Hit that like button and share if this story sparks an “aha!” moment! 🚀


    I’m running a bakery. My job is to bake cakes—simple, right? But sometimes, people ask for extra things with their cakes: a fancy ribbon, a handwritten note, or a gift wrap. Now, baking cakes is my core job, but these extra requests are “side effects” that need handling.

    To manage this, I’ve got a trusty assistant. Let’s call them NgRx Effects. When someone places an order, I focus on baking while passing all the extra requests to my assistant. They handle the ribbons, notes, and gift wrap without me losing focus on baking.

    Here’s the cool part: my assistant is really organized. They don’t just run off willy-nilly; they follow a clear plan. For every extra task (like adding a ribbon), they know what to do, when to do it, and how to update me when they’re done. If the customer says, “Make this a surprise delivery,” my assistant coordinates with the delivery team while I keep baking.

    So in the world of NgRx, I’m the Reducer, focusing on updating the store (baking the cake). The side effects, like fetching data from a server or triggering notifications, go to Effects. They listen for specific actions I dispatch and execute those extra tasks without disrupting my flow.

    NgRx Effects let me stay productive and focused, ensuring every cake (or app state update) comes out perfect, while the side effects are handled seamlessly.


    Connecting the Story to JavaScript

    In our bakery, the baking cakes part is like the Reducer function—it takes an action (order) and updates the store (cake). The assistant managing side effects is like the NgRx Effect. Here’s what it looks like in code:

    The Reducer (Baking Cakes)

    import { createReducer, on } from '@ngrx/store';
    import { placeOrderSuccess } from './actions';
    
    export const initialState = { orders: [] };
    
    const orderReducer = createReducer(
      initialState,
      on(placeOrderSuccess, (state, { order }) => ({
        ...state,
        orders: [...state.orders, order],
      }))
    );
    
    export function reducer(state, action) {
      return orderReducer(state, action);
    }
    • This reducer focuses solely on updating the state—it doesn’t handle side effects like fetching orders from an API or sending notifications.

    The Effect (Assistant Handling Side Effects)

    import { Injectable } from '@angular/core';
    import { Actions, createEffect, ofType } from '@ngrx/effects';
    import { of } from 'rxjs';
    import { map, switchMap, catchError } from 'rxjs/operators';
    import { placeOrder, placeOrderSuccess, placeOrderFailure } from './actions';
    import { OrderService } from './order.service';
    
    @Injectable()
    export class OrderEffects {
      constructor(private actions$: Actions, private orderService: OrderService) {}
    
      placeOrder$ = createEffect(() =>
        this.actions$.pipe(
          ofType(placeOrder), // Listen for the "placeOrder" action
          switchMap(({ orderDetails }) =>
            this.orderService.placeOrder(orderDetails).pipe(
              map((order) => placeOrderSuccess({ order })), // Dispatch success action
              catchError((error) => of(placeOrderFailure({ error }))) // Dispatch failure action
            )
          )
        )
      );
    }
    • ofType(placeOrder): This tells the assistant to act only on specific tasks (like gift-wrapping orders).
    • switchMap: Handles the asynchronous task of sending the order to an API.
    • Dispatch actions: When the task is done, the effect dispatches either a success or failure action to keep everything updated.

    How It Comes Together

    1. A user dispatches the placeOrder action.
    2. The Reducer ignores side effects and only handles the store.
    3. The Effect listens to the placeOrder action, calls the OrderService, and updates the store by dispatching a placeOrderSuccess or placeOrderFailure action.

    Key Takeaways

    1. Reducers are pure functions: They only update the state based on the actions they receive.
    2. Effects handle side effects: They manage tasks like API calls, logging, or notifications, ensuring reducers stay clean.
    3. Decoupling responsibilities: This separation keeps your app scalable, testable, and maintainable.