Angular 14中NGRX Selector获取Email返回Undefined问题排查
问题描述
组件构造函数外定义:
userEmail = this.Authstate.pipe(select(selectUserInfo));
构造函数内分发动作:
this.Authstate.dispatch(unencryptEmail({ email: this.userKey }));
在ngOnInit中订阅:
this.userEmail.subscribe(console.log);
此时状态和控制台输出正常,但修改订阅为this.userEmail.subscribe(cc => console.log(cc?.LoggedInUser?.email));后,控制台返回undefined。
在reducer的unencryptEmailComplete方法中,打印profile: + profile输出profile: [object object],打印profile: + profile.LoggedInUser则输出profile: undefined,无法获取Email。
补充代码
Selector代码
export const getAuthFeatureState = createFeatureSelector<AuthState>(authFeatureName); export const selectUser = (state: AuthState) => state.profile; export const selectIsAuthenticated = createSelector( getAuthFeatureState, (state: AuthState) => state.isLoggedIn ); export const selectUserInfo = createSelector( getAuthFeatureState, (state: AuthState) => { return state.profile } );
模型定义
export interface User { firstname: string, surname: string, email: string, phone: number, userId?: number, sex?: string, dateOfBirth?: Date, username: string, password?: string } export interface LoginUser { email: string, password: string } export interface LoggedInUser { LoggedInUser: User, token?: string }
Reducer代码
export const authFeatureName = 'auth'; export interface AuthState { profile: LoggedInUser | null; isLoggedIn: boolean; } export const initialAuthState: AuthState = { profile: null, isLoggedIn: false, }; const authReducerInternal = createReducer( initialAuthState, on(authActions.loginComplete, (state, { profile, isLoggedIn }) => { return { ...state, profile, isLoggedIn, }; }), on(authActions.logout, (state, { }) => { return { ...state, profile: null, isLoggedIn: false, }; }), on(authActions.unencryptEmailComplete, (state, { profile, isLoggedIn }) => { console.log("unencryptEmail complete action"); return { ...state, profile, isLoggedIn, }; }) );
Effect代码
unencryptEmail$ = createEffect( () => this.actions$.pipe( ofType(fromAuthActions.unencryptEmail), mergeMap((auth) => { return this.authService.doUnencryptEmail(auth.email) .pipe(map((data: LoggedInUser) => { return fromAuthActions.unencryptEmailComplete({ profile: data, isLoggedIn: false }); })) }) ), { dispatch: true } );
原因分析及解决方案
核心原因
authService.doUnencryptEmail返回的数据结构和你定义的LoggedInUser接口不匹配。
从reducer的打印结果能明确:
- 直接打印
profile显示是对象,但访问profile.LoggedInUser得到undefined,说明返回的对象里根本没有LoggedInUser这个嵌套属性——你预期的是{ LoggedInUser: User }结构,但实际接口返回的很可能是直接的User对象(包含email、firstname等字段的扁平结构)。
解决方案(二选一即可)
方案一:调整Effect的数据映射逻辑(推荐)
把接口返回的User数据包装成符合LoggedInUser接口的结构:
unencryptEmail$ = createEffect( () => this.actions$.pipe( ofType(fromAuthActions.unencryptEmail), mergeMap((auth) => { return this.authService.doUnencryptEmail(auth.email) // 将返回的User数据包装为LoggedInUser结构 .pipe(map((userData: User) => { return fromAuthActions.unencryptEmailComplete({ profile: { LoggedInUser: userData }, isLoggedIn: false }); })) }) ), { dispatch: true } );
方案二:调整接口与状态结构
如果接口确实返回扁平的User对象,没必要强行嵌套,可以简化结构:
- 修改
LoggedInUser接口:
export interface LoggedInUser extends User { token?: string }
- 同步更新
AuthState的profile类型:
export interface AuthState { profile: LoggedInUser | null; isLoggedIn: boolean; }
- 修改订阅代码:
this.userEmail.subscribe(cc => console.log(cc?.email));
内容的提问来源于stack exchange,提问作者bilpor
相关产品推荐
相关产品推荐

