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

如何使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 23:35:16