Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

angular ngrx-store-localstorage don't stored data after reload

I'm trying to integrate ngrx into my authentication process using ngrx-store-localstorage to store the token across browser sessions.

When I do login I can see the value of the token in the storage like

{"token":{"token":"e5cb6515-149c-44df-88d1-4ff16ff4169b","expiresIn":10385,"expiresAt":1512082478397},"authenticated":true,"error":false}

but when for instance I edit a page and the app reload I've got

{"token":null,"authenticated":false,"error":false}

The code actions

import { Action } from '@ngrx/store';

import { AuthenticationTokenInterface } from '../authentication.interface';

export const LOGIN = 'LOGIN';
export const LOGIN_SUCCESS = 'LOGIN_SUCCESS';
export const LOGIN_FAILED = 'LOGIN_FAILED';
export const LOGOUT = 'LOGOUT';
export const SET_TOKEN = 'SET_TOKEN';

export class Login implements Action {
  readonly type = LOGIN;
  constructor(public payload: {username: 'string',password: 'string'}) {}
}

export class LoginSuccess implements Action {
  readonly type = LOGIN_SUCCESS;
}

export class LoginFailed implements Action {
  readonly type = LOGIN_FAILED;
}

export class Logout implements Action {
  readonly type = LOGOUT;
}

export class SetToken implements Action {
  readonly type = SET_TOKEN;

  constructor(public payload: AuthenticationTokenInterface) {}
}

export type AuthenticationActions =  Login | LoginSuccess | LoginFailed | Logout | SetToken;

effects

@Injectable()
export class AuthenticationEffects {
  @Effect()
  login$ = this.actions$.ofType(AuthenticationActions.LOGIN)
  .pipe(
    mergeMap((action: AuthenticationActions.Login) => {
       return this.authenticationService.login(action.payload)
       .pipe(
        mergeMap((token:AuthenticationTokenInterface) => {
          this.router.navigate(['/main/dashboard']);
          return [
            { type: AuthenticationActions.LOGIN_SUCCESS },
            { type: AuthenticationActions.SET_TOKEN, payload: token }
          ]
        }),
        catchError(() => {
          return  of({ type: AuthenticationActions.LOGIN_FAILED })
        })
       )
     })
  );
  @Effect({dispatch: false})
  logout$ = this.actions$.ofType(AuthenticationActions.LOGOUT)
  .pipe(
    switchMap(()=>{
      this.router.navigate(['/logout']);
      return this.authenticationService.logout();
    })
  );

  constructor(
    private actions$: Actions,
    private router: Router,
    private authenticationService: AuthenticationService
  ) {}
}

reducers

export interface State {
  token: AuthenticationTokenInterface;
  authenticated: boolean;
  error: boolean;
}

const initialState: State = {
  token: null,
  authenticated: false,
  error: false
};

export function authenticationReducer(
  state = initialState,
  action: AuthenticationActions.AuthenticationActions
):State {
  switch (action.type) {
    case (AuthenticationActions.LOGIN_SUCCESS):
      return {
        ...state,
        authenticated: true,
      };
    case (AuthenticationActions.LOGIN_FAILED):
      return {
        ...state,
        error: true
      };
    case (AuthenticationActions.LOGOUT):
      return {
        ...state,
        token: null,
        authenticated: false
      };
    case (AuthenticationActions.SET_TOKEN):
      return {
        ...state,
        token: action.payload
      };
    default:
      return state;
  }
}

reducers (App)

export interface AppState {
  authentication: fromAuthentication.State
}

export const reducers: ActionReducerMap<AppState> = {
  authentication: fromAuthentication.authenticationReducer
};

App module

export function localStorageSyncReducer(reducer: ActionReducer<any>): ActionReducer<any> {
  return localStorageSync({keys: ['authentication']})(reducer);
}
const metaReducers: Array<MetaReducer<any, any>> = [localStorageSyncReducer];

imports: [
  StoreModule.forRoot(
      reducers,
      {metaReducers}
  ),
  EffectsModule.forRoot([AuthenticationEffects]),
]
like image 664
Whisher Avatar asked Nov 30 '17 20:11

Whisher


People also ask

Does NgRx store data in local storage?

The Use Case for LocalStorage NgRx is an amazing tool for building applications in Angular. However, from time to time, we find ourselves with a requirement that isn't handled easily with NgRx. Primarily, in cases where we want state to be persisted beyond a browser session but not stored in a database.

How do I keep NgRx state from refreshing?

When you install the Redux DevTools addon in your browser while instrumenting your store with @ngrx/store-devtools you'll be able to persist the state and action history between page reloads. You can't really ask your users to install a browser extension.

How do I persist data in NgRx?

Currently ngrx/store doesn't support such functionality. But you can maintain state by using a library like ngrx-store-persist to save data to the browsers indexedDB, localStorage or WebStorage. This library automatically saves and restores ngrx/store's data.

Where does NgRx store its data?

Where Does NgRx Store Data? NgRx stores the application state in an RxJS observable inside an Angular service called Store. At the same time, this service implements the Observable interface.


1 Answers

You need to add rehydrate key to the localStorageSync function call.

export function localStorageSyncReducer(reducer: ActionReducer<any>): ActionReducer<any> {
  return localStorageSync(
     {keys: ['authentication'],
      rehydrate: true})(reducer);
}

See: https://github.com/btroncone/ngrx-store-localstorage/blob/master/README.md for details.

like image 147
Mateusz Witkowski Avatar answered Sep 28 '22 02:09

Mateusz Witkowski