Angular 7 used NGRX and save token in localStorage

Pionas picture Pionas · Nov 29, 2018 · Viewed 7.4k times · Source

In my Angular application i used NGRX store and i have some problem with saved user token. Sometimes i reload my page and lost everything.

In app.component.ts implement OnInit and add there:

this.store.select('auth').subscribe(event => {
  if (event.token) {
    window.localStorage.setItem('token', JSON.stringify(event.token));
  }
});

if (window.localStorage.getItem('token')) {
  const token = JSON.parse(window.localStorage.getItem('token'));
  this.store.dispatch(new AuthActions.SetToken(token));
}

And created Effect:

@Effect()
this.actions$.pipe(
    ofType<AuthActions.TrySignin> (
        AuthActions.AuthActionTypes.TRY_SIGNIN
    ),
        switchMap(action => {
            return this.httpClient.put('http://localhost:8080/api/signin', {
                username: action.payload.username,
                password: action.payload.password
            }, {
                    observe: 'body',
                    responseType: 'text'
                }).pipe(
                    map(
                        token => {
                            this.router.navigate(['/']);
                            return new AuthActions.SetToken(token);
                        }
                    ),
                    catchError(error => {
                        return of(new AuthActions.AuthFailed(error));
                    }
                    )
                );

        }
        )
);

It is correct?

Answer

Lars picture Lars · Mar 21, 2019

By default, your application state gets reset when you refresh the page.

What you need to do is, save your 'auth' state to a persistant storage, like localstorage/sessionstorage.

And restore the state from localstorage/sessionstorage on startup.

I've created a library to handle this for you easily: https://github.com/larscom/ngrx-store-storagesync

  1. Run:

npm install --save @larscom/ngrx-store-storagesync

  1. Configure the meta reducer

The configuration would be something like the following for your setup

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { StoreModule, ActionReducerMap, ActionReducer, MetaReducer } from '@ngrx/store';
import { storageSync } from '@larscom/ngrx-store-storagesync';
import * as fromAuth from './auth/reducer';

export const reducers: ActionReducerMap<ISomeState> = { 
  auth: fromAuth.reducer
};

export function storageSyncReducer(reducer: ActionReducer<any>): ActionReducer<any> {
  return storageSync<ISomeState>({
    features: [
      // saves the auth state to sessionStorage
      { stateKey: 'auth' }
    ],     
    storage: window.sessionStorage
  })(reducer);
}

const metaReducers: Array<MetaReducer<any, any>> = [storageSyncReducer];

@NgModule({
  imports: [BrowserModule, StoreModule.forRoot(reducers, { metaReducers })]
})
export class AppModule {}

That's it, if you reload the page, the state will restore from the sessionStorage (in this case)