如何使用NgRx创建异步Reducer?Angular API调用遇Promise问题
问题分析
你遇到的核心问题是NgRx的Reducer必须是同步纯函数,不能在里面执行异步操作(比如API调用)。returnDatas是async函数,调用它会直接返回Promise,而非等待异步操作完成后的实际响应——这既是异步函数的固有特性,也违反了Reducer的设计原则。
解决方案:用NgRx Effects处理异步逻辑
正确的做法是把API调用这类异步逻辑放到NgRx Effects中,让Effects处理异步操作,完成后再触发对应的Action来更新Reducer的状态。具体步骤如下:
1. 补充Action定义
新增两个Action,分别对应API调用成功和失败的场景:
import { createAction, props } from '@ngrx/store'; export const loadFiles = createAction( '[Files] Load Files', props<{ data: any }>() ); export const loadFilesSuccess = createAction( '[Files] Load Files Success', props<{ files: any[] }>() ); export const loadFilesFailure = createAction( '[Files] Load Files Failure', props<{ error: any }>() );
2. 创建Effects处理异步API调用
新建files.effects.ts文件,把API调用逻辑移到这里:
import { Injectable } from '@angular/core'; import { Actions, createEffect, ofType } from '@ngrx/effects'; import { of } from 'rxjs'; import { catchError, map, mergeMap } from 'rxjs/operators'; import axios from 'axios'; import * as FilesActions from './files.actions'; @Injectable() export class FilesEffects { loadFiles$ = createEffect(() => this.actions$.pipe( ofType(FilesActions.loadFiles), mergeMap((action) => { const form = new FormData(); form.append('file', action.data); return axios.post('http://localhost:8080/api/v1/uploads', form).pipe( map((res) => FilesActions.loadFilesSuccess({ files: res.data })), catchError((error) => of(FilesActions.loadFilesFailure({ error }))) ); }) ) ); constructor(private actions$: Actions) {} }
3. 修改Reducer处理新的Action
更新Reducer,让它响应loadFilesSuccess和loadFilesFailure动作来更新状态:
import { createReducer, on } from '@ngrx/store'; import * as FilesActions from './files.actions'; export const initialState = { files: JSON.parse(sessionStorage.getItem('files') || '[]'), test: 0, error: null }; export const filesReducer = createReducer( initialState, on(FilesActions.increment, (state) => ({ ...state, test: state.test + 1 })), on(FilesActions.loadFilesSuccess, (state, action) => ({ ...state, files: action.files, error: null })), on(FilesActions.loadFilesFailure, (state, action) => ({ ...state, error: action.error })) );
4. 注册Effects
在模块中注册Effects,比如app.module.ts:
import { NgModule } from '@angular/core'; import { EffectsModule } from '@ngrx/effects'; import { FilesEffects } from './files.effects'; @NgModule({ imports: [ // 其他导入项 EffectsModule.forRoot([FilesEffects]) ] }) export class AppModule {}
关键说明
- Reducer只负责同步更新状态,必须是纯函数,不能包含任何副作用(API调用、本地存储读写、定时器等)
- Effects专门处理异步逻辑和副作用,完成后通过触发Action通知Reducer更新状态
- 异步操作的结果永远不能直接在Reducer中获取,必须通过Action传递
内容的提问来源于stack exchange,提问作者gsk 42
相关产品推荐
相关产品推荐

