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

使用正确账号密码登录时NgRx状态管理出现异常

问题分析与修复方案

1. 无限循环与Success Effect未触发的核心原因

你的loginSuccess$ Effect未配置dispatch: false,默认情况下createEffect会自动dispatch流中发出的内容。但该Effect仅包含tap操作(仅执行副作用,无返回值),导致它会发出undefined,NgRx会尝试dispatch这个无效的undefined action,进而触发Reducer无限执行,同时干扰了正常的loginSuccess action处理流程。

修复:给loginSuccess$添加{ dispatch: false }配置:

loginSuccess$ = createEffect(
    () => 
        this.actions$.pipe(
            ofType(AuthActions.loginSuccess),
            tap(({ loginResponse }) => {
                this.router.navigateByUrl('/register');
            })
        ),
    { dispatch: false } // 关键配置:不dispatch任何action
);

2. Reducer状态结构不匹配

初始状态initialAuthState为LoginResponse类型,但该类型未包含isLoading、error字段,Reducer处理action时强行添加这些字段会导致状态类型不一致;同时loginSuccess的处理逻辑错误地将字符串类型的loginResponse(仅firstName)赋值给状态,无法正确更新完整用户信息。

修复:

  • 创建包含状态字段的AuthState类型,扩展原有用户信息结构:
export interface AuthState {
  user: LoginResponse;
  isLoading: boolean;
  error: string | null;
}

const initialAuthState: AuthState = {
  user: {
    username: "",
    firstName: "",
    token: "",
    lastName: "",
    photoUrl: ""
  },
  isLoading: false,
  error: null
};
  • 修正Reducer逻辑,正确合并用户响应到状态:
export const _authReducer = createReducer(initialAuthState, 
    on(AuthActions.loginRequest, state => ({...state, isLoading: true, error: null})),
    on(AuthActions.loginSuccess, (state, { loginResponse }) => ({ 
        ...state, 
        isLoading: false, 
        user: { ...state.user, ...loginResponse } // 合并完整用户响应
    })),
    on(AuthActions.loginFailure, (state, { error }) => ({...state, isLoading: false, error}))
);

3. LoginSuccess Action的Payload类型错误

当前loginSuccess的payload仅传递了firstName字符串,无法承载完整用户信息。需修改Action,让它接收完整的LoginResponse类型。

修复login.action.ts:

import { LoginResponse } from "../../_models/LoginResponse";

export const loginSuccess = createAction(
    '[Login] Login Success',
    props<{ loginResponse: LoginResponse }>() // 改为完整的LoginResponse类型
)

同时修正Effect中loginSuccess的dispatch逻辑,传递完整响应:

map((loginResponse) => AuthActions.loginSuccess({ loginResponse })), // 传递完整响应而非仅firstName

4. 关键语法错误(Effect未被正确注入)

你的AuthEffects类漏写了@符号,Injectable()应改为@Injectable(),否则Angular无法识别并注入该Effect,这也是Effect不触发的重要原因。

5. 次要拼写错误

loginFailure的action类型拼写错误:[Login] Login Failuer → 改为[Login] Login Failure,避免后续调试混淆。


修正后的关键代码片段

login.action.ts

import { createAction, props } from "@ngrx/store";
import { LoginResponse } from "../../_models/LoginResponse";

export const loginRequest = createAction(
    '[Login] Login Request',
    props<{ credential: { emailId: string; password: string }}>()
)

export const loginSuccess = createAction(
    '[Login] Login Success',
    props<{ loginResponse: LoginResponse }>()
)

export const loginFailure = createAction(
    '[Login] Login Failure',
    props<{ error: string }>()
)

login.effect.ts

import { inject, Injectable } from "@angular/core";
import { Actions, createEffect, ofType } from "@ngrx/effects";
import { AccountService } from "../../_services/account.service";
import { Router } from "@angular/router";
import * as AuthActions from '../actions/login.action';
import { catchError, exhaustMap, map, of, tap } from "rxjs";

@Injectable()
export class AuthEffects {

    private actions$ = inject(Actions);
    private authService = inject(AccountService);
    private router = inject(Router);

    loginRequest$ = createEffect(() =>
        this.actions$.pipe(
            ofType(AuthActions.loginRequest),
            exhaustMap((action) => 
                this.authService
                    .login({userName: action.credential.emailId, password: action.credential.password})
                    .pipe(
                        map((loginResponse) => AuthActions.loginSuccess({ loginResponse })),
                        catchError((error) => of(AuthActions.loginFailure({error: error.message})))
                    )
                )
            )
        );

    loginSuccess$ = createEffect(
        () => 
            this.actions$.pipe(
                ofType(AuthActions.loginSuccess),
                tap(() => {
                    this.router.navigateByUrl('/register');
                })
            ),
        { dispatch: false }
    );
}

login.reducer.ts

import { Action, createReducer, on } from "@ngrx/store";
import { LoginResponse } from "../../_models/LoginResponse";
import * as AuthActions from '../actions/login.action';

export interface AuthState {
  user: LoginResponse;
  isLoading: boolean;
  error: string | null;
}

const initialAuthState: AuthState = {
  user: {
    username: "",
    firstName: "",
    token: "",
    lastName: "",
    photoUrl: ""
  },
  isLoading: false,
  error: null
};

export const _authReducer = createReducer(initialAuthState, 
    on(AuthActions.loginRequest, state => ({...state, isLoading: true, error: null})),
    on(AuthActions.loginSuccess, (state, { loginResponse }) => ({ 
        ...state, 
        isLoading: false, 
        user: { ...state.user, ...loginResponse }
    })),
    on(AuthActions.loginFailure, (state, { error }) => ({...state, isLoading: false, error}))
);

export function authReducer(state: AuthState | undefined, action: Action)
{
    console.log('State: ', state);
    console.log('Action: ', action);
    return _authReducer(state, action);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 07:38:12