๐Ÿ”ฅ Persisting Shared State in Angular Micro Frontends (MFEs) using NgRx + localStorage

Persisting Shared State in Angular MFEs with NgRx and localStorage ensures state remains intact after page reloads. By using NgRx MetaReducers

ยท

2 min read

When using NgRx across Micro Frontends, the state resets on page reload because it's stored in memory.
โœ… To persist the state across reloads, weโ€™ll use localStorage with NgRx MetaReducers.


๐Ÿ“Œ Step 1: Create a MetaReducer to Sync State with localStorage

Modify projects/mfe1/src/app/store/local-storage.reducer.ts:

๐Ÿ”น Define MetaReducer

import { ActionReducer, INIT, UPDATE } from '@ngrx/store';

export function localStorageMetaReducer<S>(reducer: ActionReducer<S>): ActionReducer<S> {
  return (state, action) => {
    if (action.type === INIT || action.type === UPDATE) {
      const savedState = localStorage.getItem('appState');
      return savedState ? JSON.parse(savedState) : state;
    }

    const nextState = reducer(state, action);
    localStorage.setItem('appState', JSON.stringify(nextState));
    return nextState;
  };
}

๐Ÿ” How it Works:

  1. On App Start (INIT), it checks if state exists in localStorage and loads it.

  2. On Any State Update, it saves the latest state to localStorage.


๐Ÿ“Œ Step 2: Apply MetaReducer in MFE1

Modify projects/mfe1/src/app/app.module.ts:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { StoreModule, MetaReducer } from '@ngrx/store';
import { counterReducer } from './store/counter.reducer';
import { localStorageMetaReducer } from './store/local-storage.reducer';
import { AppComponent } from './app.component';

const metaReducers: MetaReducer<any>[] = [localStorageMetaReducer];

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    StoreModule.forRoot({ count: counterReducer }, { metaReducers }), // Apply MetaReducer
  ],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

โœ… Now, MFE1 automatically saves state to localStorage and restores it on page reload.


๐Ÿ“Œ Step 3: Apply MetaReducer in Shell App

Modify projects/shell/src/app/app.module.ts:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { StoreModule, MetaReducer } from '@ngrx/store';
import { counterReducer } from 'mfe1/Store'; 
import { localStorageMetaReducer } from 'mfe1/Store'; // Import from remote

const metaReducers: MetaReducer<any>[] = [localStorageMetaReducer];

@NgModule({
  declarations: [],
  imports: [
    BrowserModule,
    StoreModule.forRoot({ count: counterReducer }, { metaReducers }), // Apply MetaReducer
  ],
  providers: [],
  bootstrap: [],
})
export class AppModule {}

โœ… Now, the Shell App also persists and loads state from localStorage!


๐Ÿ“Œ Step 4: Test the Persistent State

  1. Run MFE1:

     ng serve mfe1 --port 4201
    
  2. Run Shell App:

     ng serve shell --port 4200
    
  3. Open http://localhost:4200/

  4. Increment/Decrement the counter

  5. Refresh the page
    โœ… State remains intact! ๐ŸŽ‰


๐Ÿ”ฅ Final Summary

โœ… NgRx MetaReducers ensure persistent state storage.
โœ… localStorage maintains shared state between MFEs and Shell App.
โœ… Cross-MFE state management is seamless with Module Federation.

ย