myHotTake

Tag: Feature module state

  • How to Manage State in Lazy-Loaded Angular Modules with NgRx?

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


    So, picture this: I’m the proud owner of a estate, and each room in my mansion represents a different feature of an app. Now, my mansion is so large that I don’t want to keep every room fully furnished all the time, because it’s just too much to manage. That’s where the concept of lazy-loading comes in. I only bring out the furniture for each room when I’m ready to entertain guests in that particular area.

    Now, state management is like the butler of my mansion. This butler is responsible for keeping track of everything—where the furniture is, what needs to be cleaned, and who’s visiting which room. In my case, this butler is NgRx. He makes sure that every time I open a room, the right furniture and decor are set up just as I like them.

    But here’s the twist: each room might have its unique style and requirements, so my butler has a special way of managing this. When I decide I want to open a room (or in app terms, a feature module), the butler doesn’t rush to set everything up at once. Instead, he waits for the cue—like when a guest walks into the room. This is the lazy-loading part.

    Once the guest enters, the butler quickly and efficiently sets up the room’s state, pulling out the right chairs, tables, and decorations from storage (the store) and placing them where they need to be. This way, I ensure that my estate runs smoothly and efficiently, without wasting resources on rooms that aren’t in use.

    And just like that, I maintain a well-organized and efficient mansion, with the butler—my trusty NgRx—handling the state of each room seamlessly, ensuring that everything is in place exactly when it’s needed. So, in the world of my app mansion, lazy-loaded modules and NgRx work together like a charm to create a harmonious and efficient living space.


    In our mansion, the rooms are the lazy-loaded modules. In JavaScript, we achieve this by using Angular’s loadChildren property in the routing configuration to load these modules only when needed. Here’s an example:

    const routes: Routes = [
      {
        path: 'feature',
        loadChildren: () =>
          import('./feature/feature.module').then((m) => m.FeatureModule),
      },
    ];

    This is like deciding which room to open for the guests, ensuring that the room is only set up when someone enters.

    Now, the butler, who manages everything, is represented by the NgRx store. For each lazy-loaded module, we create a feature state. This can be done by defining feature-specific actions, reducers, and selectors. When a module is loaded, we dynamically register its reducer with the store. Here’s how:

    1. Define Actions and Reducer for the feature module:
       import { createAction, createReducer, on, props } from '@ngrx/store';
    
       export const loadFeatureData = createAction('[Feature] Load Data');
       export const loadFeatureDataSuccess = createAction(
         '[Feature] Load Data Success',
         props<{ data: any }>()
       );
    
       export interface FeatureState {
         data: any;
       }
    
       const initialState: FeatureState = {
         data: null,
       };
    
       const featureReducer = createReducer(
         initialState,
         on(loadFeatureDataSuccess, (state, { data }) => ({ ...state, data }))
       );
    
       export function reducer(state: FeatureState | undefined, action: Action) {
         return featureReducer(state, action);
       }
    1. Register the Reducer when the module is loaded:
       import { NgModule } from '@angular/core';
       import { StoreModule } from '@ngrx/store';
       import { reducer } from './feature.reducer';
    
       @NgModule({
         imports: [
           StoreModule.forFeature('feature', reducer), // Register the feature state
           // other imports
         ],
         // declarations, providers, etc.
       })
       export class FeatureModule {}

    This is akin to the butler setting up the room’s state—bringing out the right furniture and decor when the room is used.

    1. Use Selectors to access the feature state:
       import { createFeatureSelector, createSelector } from '@ngrx/store';
       import { FeatureState } from './feature.reducer';
    
       export const selectFeatureState = createFeatureSelector<FeatureState>('feature');
    
       export const selectFeatureData = createSelector(
         selectFeatureState,
         (state) => state.data
       );

    By using selectors, we can efficiently access the state to ensure everything is in place, just like how the butler knows exactly where each piece of furniture belongs.


    Key Takeaways:

    • Lazy Loading with Angular: Use loadChildren to load feature modules only when needed, optimizing resource usage.
    • Dynamic State Management with NgRx: Register feature-specific reducers when modules are loaded to manage state dynamically.
    • Selectors for State Access: Utilize selectors to efficiently retrieve and manage state within modules, ensuring a seamless user experience.

    In conclusion, managing state in lazy-loaded modules with NgRx is like efficiently running a mansion with a skilled butler, ensuring everything is perfectly in place when needed without unnecessary resource usage. This approach helps in building scalable and performant Angular applications.