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

如何用NgRx Effects优雅实现创建功能?解决请求等待与回滚问题

基于NgRx Effects的优雅创建流程

核心思路是先发起请求,再根据请求结果更新Store,彻底避免先更新Store后回滚的糟糕体验,具体流程如下:

  • 组件dispatch「创建请求」动作(比如CreateItemRequest),只携带要创建的对象数据,不触发Reducer更新Store
  • Effect监听该动作,调用HTTP服务发起创建请求
  • 请求成功时,Effect dispatch「创建成功」动作,携带后端返回的完整对象(含后端生成的ID等字段)
  • Reducer监听「创建成功」动作,将新对象加入Store
  • 请求失败时,Effect dispatch「创建失败」动作,携带错误信息
  • 组件或Reducer监听「创建失败」动作,处理错误状态(比如显示提示,无需回滚Store,因为之前没更新过)

代码示例

动作定义

export const createItemRequest = createAction(
  '[Items] Create Item Request',
  props<{ item: Partial<Item> }>()
);

export const createItemSuccess = createAction(
  '[Items] Create Item Success',
  props<{ item: Item }>()
);

export const createItemFailure = createAction(
  '[Items] Create Item Failure',
  props<{ error: string }>()
);

Effect实现

@Injectable()
export class ItemEffects {
  createItem$ = createEffect(() =>
    this.actions$.pipe(
      ofType(createItemRequest),
      mergeMap(({ item }) =>
        this.itemService.createItem(item).pipe(
          map((createdItem) => createItemSuccess({ item: createdItem })),
          catchError((error) => of(createItemFailure({ error: error.message })))
        )
      )
    )
  );

  constructor(
    private actions$: Actions,
    private itemService: ItemService
  ) {}
}

Reducer处理

export const initialState: ItemsState = {
  items: [],
  error: null,
  loading: false
};

const itemsReducer = createReducer(
  initialState,
  on(createItemRequest, (state) => ({
    ...state,
    loading: true,
    error: null
  })),
  on(createItemSuccess, (state, { item }) => ({
    ...state,
    items: [...state.items, item],
    loading: false
  })),
  on(createItemFailure, (state, { error }) => ({
    ...state,
    error,
    loading: false
  }))
);

组件处理

组件可监听Store的loading和error状态,给用户实时反馈:

@Component({...})
export class ItemCreateComponent {
  loading$ = this.store.select(selectItemsLoading);
  error$ = this.store.select(selectItemsError);

  constructor(private store: Store<AppState>) {}

  onSubmit(itemData: Partial<Item>) {
    this.store.dispatch(createItemRequest({ item: itemData }));
  }
}
是否应该直接在组件中调用服务?

如果你的创建逻辑极简单,没有跨组件共享状态的需求,也不需要利用NgRx的中间件、日志、devtools等特性,直接在组件中调用服务是更轻量的选择:

@Component({...})
export class ItemCreateComponent {
  loading = false;
  error: string | null = null;

  constructor(private itemService: ItemService) {}

  async onSubmit(itemData: Partial<Item>) {
    this.loading = true;
    this.error = null;
    try {
      const createdItem = await this.itemService.createItem(itemData).toPromise();
      // 处理成功逻辑,比如跳转列表页或更新本地状态
    } catch (error) {
      this.error = error.message;
    } finally {
      this.loading = false;
    }
  }
}

但如果项目已经大规模使用NgRx,或者创建操作需要和其他状态联动(比如创建后更新多个模块状态),或者需要统一的错误处理、加载状态管理,那坚持用NgRx Effects的方案更一致,也更易于维护。

内容的提问来源于stack exchange,提问作者Benedikt J Schlegel

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 18:22:55