background-shape
feature-image

What is NgRx?

NgRx is a framework for building reactive applications in Angular.

Why use NgRx or any statemanagement framework?

Communication between components is a key feature of a reactive frontend application. In Angular, components communicate with each other using events. This is done using the @Input and @Output decorator. Consider a chain of such props in a component, which will end up as an extraneous props. To resolve this we can maintain a store within the application and use it to maintain the state of all the components.

Install NGRX

Within the Angular project, run the following command to install NGRX:

1
npm install @ngrx/{effects,entity,router-store,store,store-devtools} --save

Once that is installed, now lets install ngrx-store-localstorage to persist the state of the store in the browser.

1
npm install ngrx-store-freeze ngrx-store-localstorage --save

Create the store globally

src/app/store/index.ts

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
export interface State {
  root: AppState;
}

export const reducers: ActionReducerMap<State> = {
  root: CoreReducer,
};

export const coreInitState: State = {
  root: APP_STATE,
};
const reducerKeys = ["root"];
export function debug(reducer: ActionReducer<any>): ActionReducer<any> {
  return function (state, action) {
    return reducer(state, action);
  };
}

export function localStorageSyncReducer(
  reducer: ActionReducer<any>
): ActionReducer<any> {
  return localStorageSync({ keys: reducerKeys })(reducer);
}

export const metaReducers: MetaReducer<State>[] = !environment.production
  ? [debug, localStorageSyncReducer]
  : [localStorageSyncReducer];

export const appState = (state: any): AppState => state.root;

Import the store

src/app/app.module.ts

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    AppRoutingModule,
    BrowserAnimationsModule,
    StoreModule.forRoot(reducers, { metaReducers }),
    StoreDevtoolsModule.instrument({
      maxAge: 25,
      logOnly: environment.production,
    }),
  ],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

Feature Store

Once we create a root store, we can extend the store for multiple feature within a feature module of the application. For example, let us now consider having a feature module for User module.

We’ll need a action and reducer to begin with. Remember that the source in createActionGroup and name in createFeature should be same.

src/app/user/user.action.ts

1
2
3
4
5
6
7
export const flow = createActionGroup({
  source: 'user',
  events: {
    'Create User': props<UserState>();
  }
})

src/app/user/user.reducer.ts

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
interface UserState{
  firstName: string | null,
  lastName?: string | null,
  age: number | null
}

const initialState: UserState {
  firstName: null,
  age: null
}

export const userFeature = createFeature({
  name: 'user',
  reducer: createReducer({
    initialState,
    on(UserActions.createUser, (state, payload) => ({...state, ...payload}))
  })
})

Once done with the reducer we’ll have it imported in the UserModule.

src/app/user/user.module.ts

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@NgModule({
  ...
  imports:[
    ...
    StoreModule.forFeature(userFeature)
    ...
  ]
  ...
})
export class UserModule {}