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

如何在NgRx Effects中实现多后续API调用并存储原数据

解决方案

要实现同时存储原始字符串数组、并为每个元素发起API请求保存响应的需求,你需要分三步修改NgRx代码:扩展Action定义、更新Effects逻辑、编写对应的Reducer处理状态更新。

1. 扩展Action类型

首先在data.actions.ts中新增所需的Action,分别用于保存原始数组、单个元素请求成功/失败:

// data.actions.ts
import { createAction, props } from '@ngrx/store';

// 触发数据加载的初始动作(保留原有逻辑)
export const loadData = createAction('[Data] Load Data', props<{ payload: string }>());

// 保存原始字符串数组的动作
export const loadRawDataSuccess = createAction('[Data] Load Raw Data Success', props<{ rawData: string[] }>());

// 单个元素API请求成功的动作
export const loadItemSuccess = createAction('[Data] Load Item Success', props<{ itemId: string; itemData: any }>());

// 单个元素API请求失败的动作
export const loadItemFailure = createAction('[Data] Load Item Failure', props<{ itemId: string; error: string }>());

// 全局加载失败动作(保留原有逻辑)
export const loadDataFailure = createAction('[Data] Load Data Failure', props<{ error: string }>());

2. 更新Effects逻辑

修改DataEffects,在拿到初始数组后先派发保存原始数据的Action,再遍历每个元素发起独立API请求:

import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { HttpClient } from '@angular/common/http';
import { switchMap, map, catchError, mergeMap, tap } from 'rxjs/operators';
import { of } from 'rxjs';
import { 
  loadData, 
  loadRawDataSuccess, 
  loadItemSuccess, 
  loadItemFailure, 
  loadDataFailure 
} from './data.actions';

@Injectable()
export class DataEffects {
  loadData$ = createEffect(() =>
    this.actions$.pipe(
      ofType(loadData),
      switchMap((action) =>
        this.http.get<string[]>(`/api/data/${action.payload}`).pipe(
          // 第一步:派发Action保存原始字符串数组到Store
          tap((rawData) => this.actions$.next(loadRawDataSuccess({ rawData }))),
          // 第二步:遍历数组每个元素,发起单个API请求
          mergeMap((rawData) => 
            rawData.map((itemId) => 
              this.http.get(`/api/items/${itemId}`).pipe(
                map((itemData) => loadItemSuccess({ itemId, itemData })),
                catchError((error) => of(loadItemFailure({ itemId, error: error.message })))
              )
            )
          ),
          // 捕获初始数据加载的全局错误
          catchError((error) => of(loadDataFailure({ error: error.message })))
        )
      )
    )
  );

  constructor(private actions$: Actions, private http: HttpClient) {}
}

关键逻辑说明:

  • 使用tap操作符在获取初始数组后立即派发loadRawDataSuccess,确保原始字符串数组被存入Store。
  • 使用mergeMap遍历数组元素,为每个元素发起独立的API请求,每个请求的结果会触发对应的成功/失败Action。
  • 单个元素的请求错误不会中断整个流程,每个错误都会被捕获并派发loadItemFailure。

3. 编写Reducer处理状态更新

在data.reducer.ts中定义状态结构,并处理所有新增的Action:

// data.reducer.ts
import { createReducer, on } from '@ngrx/store';
import { 
  loadRawDataSuccess, 
  loadItemSuccess, 
  loadItemFailure, 
  loadDataFailure 
} from './data.actions';

export interface DataState {
  rawData: string[];          // 存储原始字符串数组
  items: Record<string, any>; // 存储单个元素的API响应,key为元素ID
  itemErrors: Record<string, string>; // 存储单个元素的请求错误
  globalError: string | null; // 存储初始数据加载的全局错误
}

export const initialState: DataState = {
  rawData: [],
  items: {},
  itemErrors: {},
  globalError: null
};

export const dataReducer = createReducer(
  initialState,
  // 处理原始数组保存
  on(loadRawDataSuccess, (state, { rawData }) => ({
    ...state,
    rawData,
    globalError: null
  })),
  // 处理单个元素请求成功
  on(loadItemSuccess, (state, { itemId, itemData }) => ({
    ...state,
    items: { ...state.items, [itemId]: itemData },
    itemErrors: { ...state.itemErrors, [itemId]: undefined } // 清除该元素的历史错误
  })),
  // 处理单个元素请求失败
  on(loadItemFailure, (state, { itemId, error }) => ({
    ...state,
    itemErrors: { ...state.itemErrors, [itemId]: error }
  })),
  // 处理全局加载失败
  on(loadDataFailure, (state, { error }) => ({
    ...state,
    globalError: error
  }))
);

这样就能同时满足两个需求:原始字符串数组被存入rawData字段,每个元素的API响应会被存入items对象中,错误信息分别存储在对应字段里。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 08:34:55