15 May 2020
Angular: Reset an NgRx 8 state using a Meta-Reducer
As with all topics related to software development, there is always more than one solution to a problem. The following proposition is simply one possible solution and not intended to be definitive.
Summary:
- Create an NgRx User Action to trigger a logout
- Capture the NgRx action using a Meta-reducer
- Reset the NgRx store/state to it's
There is an abundance of documentation for both Redux and NgRx available on the internet, but so much of it is before NgRx8 and the introduction of "creator functions". The creator functions, although seem incredibly abstract on first glance, reduce boiler-plate code significantly and are a great improvement.
Unfortunately, creator functions and their usage aren't easily transposed over the established patterns for implementing NgRx and when researching solutions, it is often difficult to find an example that suits an NgRx8+ implementation.
In the case of meta-reducers, the "debug" example provided within the NgRx documentation is clear enough, but doesn't offer much in the way of "how to use this within in your unique project".
Following is a simple and practical example of how to use a meta-reducer in an NgRx8+ application that is probably applicable to every professional Angular Developer.
Firstly, create a simple action for a user logout request.
# src/app/store/users/users.actions.ts import {createAction, props} from '@ngrx/store'; export const logout = createAction( '[Users] logout request' );
You can expose all your actions concisely by exporting them as a pseudo static class. This is a helpful pattern for both Actions and Selectors.
# src/app/store/users/index.ts import * as UserActions from './users.actions'; export {UserActions};
Next, create a meta-reducer function like the NgRx debug example.
Meta-reducers run for every action that is dispatched to the store, so you need to be able to identify the specific actions you want to hook into. Compare the current action type to the action type you're looking to commandeer, then do your state manipulation as you would in a standard reducer.
In this case, we want to trigger a state reset. This is achieved by passing the state as undefined and replacing the action with INIT.
# src/app/store/index.ts import {ActionReducer, INIT, MetaReducer} from '@ngrx/store'; import {UserActions} from './users'; export function logout(reducer: ActionReducer): ActionReducer { return (state, action) => { if ( action != null && action.type === UserActions.logout.type) { return reducer( undefined, {type: INIT}); } return reducer(state, action); }; } export const metaReducers: MetaReducer[] = [ logout ];
Don't forget to add your metaReducers array to the StoreModule.
# src/app/app.module.ts @NgModule({ ... imports: [ ... StoreModule.forRoot(reducers, { metaReducers, runtimeChecks: { strictStateImmutability: true, strictActionImmutability: true } }), ... ] })
Triggering a state reset is as easy as dispatching any other NgRx Action.
# some service or component ... constructor( protected http: HttpClient, protected store: Store ) { } public logout() { this.store.dispatch( UserActions.logout() ); return this.http.post(`${environment.apiBaseUrl}/auth/logout`, {}); } ...
PurcellYoon are a team of expert Angular Developers with a passion for creating exceptional digital experiences. We are committed to delivering superior Angular Applications for all our partners and clients.