如何从NGRX Effect向组件返回值?解决无效动作报错
解决NGRX Effect向组件返回值的问题
咱们先拆解下你现在遇到的问题:你报错的核心原因有两个——
- 组件里直接把
store.dispatch()的返回值赋值给token$,但dispatch()本身只返回你派发的action对象,根本不是异步请求回来的token; - 你的Effect没有按照NGRX的规则派发合法的action,反而直接返回了token字符串,NGRX会尝试把这个字符串当作action去派发,自然会报“invalid action”的错误。
下面是标准的解决流程,完全符合NGRX的最佳实践:
1. 先定义完整的Action类型
在auth.actions.ts里补充请求成功、失败的action,这是NGRX处理异步操作的标准模式:
import { Action } from '@ngrx/store'; // 定义action类型常量 export const GET_TOKEN = '[Auth] Get Token'; export const GET_TOKEN_SUCCESS = '[Auth] Get Token Success'; export const GET_TOKEN_FAILURE = '[Auth] Get Token Failure'; // 请求触发的action export class GetToken implements Action { readonly type = GET_TOKEN; } // 请求成功的action(携带token数据) export class GetTokenSuccess implements Action { readonly type = GET_TOKEN_SUCCESS; constructor(public payload: string) {} } // 请求失败的action(携带错误信息) export class GetTokenFailure implements Action { readonly type = GET_TOKEN_FAILURE; constructor(public payload: any) {} } // 统一导出action类型 export type AuthActions = GetToken | GetTokenSuccess | GetTokenFailure;
2. 修改Effect,让它派发合法的Action
在auth.effect.ts里,异步请求完成后要派发成功/失败的action,而不是直接返回token:
import { Injectable } from '@angular/core'; import { Actions, ofType, Effect } from '@ngrx/effects'; import { of } from 'rxjs'; import { map, switchMap, catchError } from 'rxjs/operators'; import * as authActions from './auth.actions'; import { AuthService } from './auth.service'; @Injectable() export class AuthEffects { @Effect() getToken$ = this.actions$.pipe( // 监听GET_TOKEN类型的action ofType(authActions.GET_TOKEN), // 执行异步请求 switchMap(() => this.authService.getToken().pipe( // 请求成功:派发携带token的成功action map(token => new authActions.GetTokenSuccess(token)), // 请求失败:派发携带错误信息的失败action catchError(error => of(new authActions.GetTokenFailure(error))) ) ) ); constructor( private actions$: Actions, private authService: AuthService ) {} }
3. 编写Reducer,把Token存入Store
在auth.reducer.ts里处理这些action,将token和请求状态存入store:
import * as authActions from './auth.actions'; // 定义auth模块的状态结构 export interface AuthState { token: string | null; error: any | null; loading: boolean; } // 初始状态 export const initialState: AuthState = { token: null, error: null, loading: false }; export function authReducer(state = initialState, action: authActions.AuthActions): AuthState { switch(action.type) { case authActions.GET_TOKEN: return { ...state, loading: true, error: null }; case authActions.GET_TOKEN_SUCCESS: return { ...state, token: action.payload, loading: false, error: null }; case authActions.GET_TOKEN_FAILURE: return { ...state, error: action.payload, loading: false }; default: return state; } }
4. 组件从Store中获取Token
组件不再订阅dispatch()的返回值,而是通过select从store中获取状态:
import { Component, OnInit } from '@angular/core'; import { Store } from '@ngrx/store'; import * as fromAuth from './auth.reducer'; import * as authActions from './auth.actions'; @Component({ selector: 'app-auth', templateUrl: './auth.component.html', styleUrls: ['./auth.component.css'] }) export class AuthComponent implements OnInit { // 从store中选择token状态 token$ = this.store.select(state => state.auth.token); // 或者如果你定义了selector(推荐),可以写成:this.store.select(fromAuth.selectToken) constructor(private store: Store<fromAuth.AuthState>) {} ngOnInit(): void { // 只需要派发获取token的action,不需要订阅这个dispatch的返回值 this.store.dispatch(new authActions.GetToken()); } }
额外场景:如果不想把Token存入Store
如果你的业务场景不需要把token持久化在store里,也可以让Effect不派发action,直接通过service传递数据,但这种方式不符合NGRX状态集中管理的理念,仅作参考:
修改Effect:
@Effect({ dispatch: false }) // 配置为不派发action getToken$ = this.actions$.pipe( ofType(authActions.GET_TOKEN), switchMap(() => this.authService.getToken()), tap(token => { // 通过service的Subject发射token this.authService.tokenSubject.next(token); }) );
组件中订阅:
import { Subject } from 'rxjs'; // 在AuthService里定义Subject export class AuthService { tokenSubject = new Subject<string>(); getToken(): Observable<string> { // 你的请求逻辑 } } // 组件中订阅 token$ = this.authService.tokenSubject.asObservable(); ngOnInit() { this.store.dispatch(new authActions.GetToken()); }
内容的提问来源于stack exchange,提问作者Mohamed Abo Elmagd
相关产品推荐
相关产品推荐

