Reputation: 346
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?
Upvotes: 4
Views: 10973
Reputation: 776
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
npm install --save @larscom/ngrx-store-storagesync
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)
Upvotes: 3
Reputation: 62
@Injectable()
export class FormEffects {
constructor(
private actions$: Actions<Action>,
private localStorageService: LocalStorageService
) {}
@Effect({ dispatch: false })
persistForm = this.actions$.pipe(
ofType<ActionFormUpdate>(FormActionTypes.UPDATE),
tap(action =>
this.localStorageService.setItem(FORM_KEY, { form: action.payload.form })
)
);
}
Upvotes: 0
Reputation: 15505
I would suggest you to not do this inside your components. They will become harder to test, plus you could end up with the same code in different components.
Instead use you can do this inside effects as Maciej suggested, for another example see https://github.com/tomastrajan/angular-ngrx-material-starter/blob/master/src/app/examples/form/form.effects.ts#L20
But personally, I like to use a meta-reducer for this - see https://github.com/timdeschryver/ngrx-family-grocery-list/blob/master/src/app/groceries/reducers/groceries.reducer.ts#L165
For example:
export function persistStateReducer(_reducer: ActionReducer<State>) {
const localStorageKey = '__auth';
return (state: State | undefined, action: Action) => {
if (state === undefined) {
const persisted = localStorage.getItem(localStorageKey);
return persisted ? JSON.parse(persisted) : _reducer(state, action);
}
const nextState = _reducer(state, action);
localStorage.setItem(localStorageKey, JSON.stringify(nextState));
return nextState;
};
}
Upvotes: 4
Reputation: 2171
why not save token into localstorage directly in effect?
This approach has a weakness that you have to remember to subscribe to store and save token whenever it appears.
Second bad thing abouth this is, whenever auth
state emits data your's subscribe will save token even if there was correct before.
Getting token code smells good.
Upvotes: 1