You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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对象,没必要强行嵌套,可以简化结构:

  1. 修改LoggedInUser接口:
export interface LoggedInUser extends User {
  token?: string
}
  1. 同步更新AuthState的profile类型:
export interface AuthState {
  profile: LoggedInUser | null;
  isLoggedIn: boolean;
}
  1. 修改订阅代码:
this.userEmail.subscribe(cc => console.log(cc?.email));

内容的提问来源于stack exchange,提问作者bilpor

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 13:25:01