如何用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
相关产品推荐
相关产品推荐

