Angular 7 使用 NGRX 并在本地存储中保存令牌
Angular 7 used NGRX and save token in localStorage
在我的 Angular 应用程序中,我使用了 NGRX 商店,但我在保存用户令牌时遇到了一些问题。
有时我重新加载我的页面并丢失所有内容。
在 app.component.ts 中执行 OnInit 并添加:
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));
}
并创建效果:
@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));
}
)
);
}
)
);
正确吗?
为什么不直接把token存到localstorage中生效呢?
这种方法有一个弱点,你必须记住在它出现时订阅存储和保存令牌。
第二个坏处是,每当 auth
状态发出数据时,即使之前是正确的,您的订阅也会保存令牌。
获取令牌代码闻起来很香。
我建议您不要在组件内部执行此操作。
它们将变得更难测试,而且您最终可能会在不同的组件中使用相同的代码。
改为使用您可以按照 Maciej 的建议执行此内部效果,有关另一个示例,请参见 https://github.com/tomastrajan/angular-ngrx-material-starter/blob/master/src/app/examples/form/form.effects.ts#L20
但就我个人而言,我喜欢为此使用 meta-reducer - 参见 https://github.com/timdeschryver/ngrx-family-grocery-list/blob/master/src/app/groceries/reducers/groceries.reducer.ts#L165
例如:
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;
};
}
@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 })
)
);
}
默认情况下,您的应用程序状态会在您刷新页面时重置。
您需要做的是,将您的 'auth' 状态保存到持久存储中,例如 localstorage/sessionstorage。
并在启动时从 localstorage/sessionstorage 恢复状态。
我创建了一个库来为您轻松处理此问题:
https://github.com/larscom/ngrx-store-storagesync
- 运行:
npm install --save @larscom/ngrx-store-storagesync
- 配置元减速器
您的设置的配置类似于以下内容
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 {}
就是这样,如果您重新加载页面,状态将从 sessionStorage 恢复(在这种情况下)
在我的 Angular 应用程序中,我使用了 NGRX 商店,但我在保存用户令牌时遇到了一些问题。 有时我重新加载我的页面并丢失所有内容。
在 app.component.ts 中执行 OnInit 并添加:
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));
}
并创建效果:
@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));
}
)
);
}
)
);
正确吗?
为什么不直接把token存到localstorage中生效呢?
这种方法有一个弱点,你必须记住在它出现时订阅存储和保存令牌。
第二个坏处是,每当 auth
状态发出数据时,即使之前是正确的,您的订阅也会保存令牌。
获取令牌代码闻起来很香。
我建议您不要在组件内部执行此操作。 它们将变得更难测试,而且您最终可能会在不同的组件中使用相同的代码。
改为使用您可以按照 Maciej 的建议执行此内部效果,有关另一个示例,请参见 https://github.com/tomastrajan/angular-ngrx-material-starter/blob/master/src/app/examples/form/form.effects.ts#L20
但就我个人而言,我喜欢为此使用 meta-reducer - 参见 https://github.com/timdeschryver/ngrx-family-grocery-list/blob/master/src/app/groceries/reducers/groceries.reducer.ts#L165
例如:
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;
};
}
@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 })
)
);
}
默认情况下,您的应用程序状态会在您刷新页面时重置。
您需要做的是,将您的 'auth' 状态保存到持久存储中,例如 localstorage/sessionstorage。
并在启动时从 localstorage/sessionstorage 恢复状态。
我创建了一个库来为您轻松处理此问题: https://github.com/larscom/ngrx-store-storagesync
- 运行:
npm install --save @larscom/ngrx-store-storagesync
- 配置元减速器
您的设置的配置类似于以下内容
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 {}
就是这样,如果您重新加载页面,状态将从 sessionStorage 恢复(在这种情况下)