Angular中ngrx data的EntityService update每次调用触发两次HTTP请求排查
可能的原因梳理
- 自定义update方法与ngrx Data默认执行逻辑冲突
ngrx Data默认会为实体服务的CRUD方法自动注册Effects,当你调用update方法时,默认逻辑已经会派发对应的Update action,由Effects触发HTTP请求。如果你重写update方法时没有覆盖默认的Effects逻辑,同时你在自定义update内自己实现了HTTP调用,就会导致两次HTTP请求:一次是你自定义方法里的请求,一次是默认Effects触发的请求。
另外你调用update方法时传入的是完整的IPurchaseOrder实体对象,而ngrx Data约定update方法接收的是Update<T>类型(结构为{id: 实体ID, changes: 变更字段集}),参数格式不匹配可能导致默认逻辑执行异常,重复触发请求。
// 错误调用:传入完整实体 this.purchaseOrderEntityService.update(updatePurchaseOrder); // 正确调用:传入Update结构 this.purchaseOrderEntityService.update({ id: updatePurchaseOrder.id, changes: updPo // 只传变更的字段即可 });
- 数据流重复触发update调用
你的updatePurchaseOrderPrices方法依赖purchaseOrderEntityService.entities$这个状态流,当你调用update方法更新实体后,会触发entities$emit新值,即使你加了first()操作符,若流在first()执行前已经emit了两次,就会导致tap内的update逻辑执行两次。可以在tap内加打印校验执行次数:
tap((order) => { console.log('触发update逻辑', order.id); // 新增打印确认执行次数 // 原有逻辑 })
- 订阅未正确清理导致重复执行
你使用subs.sink管理订阅,但如果subs是Subscription实例的话,多次给sink赋值只会保留最后一次订阅的引用,之前的订阅如果没有取消会一直存活,当触发更新时旧订阅的逻辑也会执行,导致重复调用update。建议改为调用subs.add()来管理多个订阅:
// 错误写法:多次赋值会丢失之前的订阅引用 this.subs.sink = xxx.subscribe(); // 正确写法:统一加入订阅集合,销毁时一次性unsubscribe this.subs.add(xxx.subscribe());
- 自定义update方法返回值不符合ngrx Data约定
你重写的update方法直接返回了HTTP请求的结果,而ngrx Data要求实体服务的CRUD方法返回的 observable 需要完成对应的状态更新逻辑,如果返回的 observable 被内部和外部同时订阅,就会触发多次HTTP请求(HTTP是冷 observable,每一次订阅都会执行一次请求)。可以给HTTP请求加share()操作符,让多个订阅共享同一个请求:
update(details: Update<IPurchaseOrder>): Observable<IPurchaseOrder> { return this.http .put<IPurchaseOrder>( `${this.baseUrl}/purchaseorder/poorder/${details.id}`, { ...details.changes, } ) .pipe( map((res: IPurchaseOrder) => res), share() // 多订阅共享同一次请求 ); }
- ngrx Data模块重复注册
如果你的EntityDataModule.forRoot()或者EntityEffectsModule被重复导入(比如同时在根模块和子特性模块导入),会导致同一个Update action被两个Effects实例处理,触发两次HTTP请求。检查模块导入逻辑,确保ngrx相关的模块只在根模块注册一次,特性模块只导入EntityDataModule.forFeature()即可。
内容的提问来源于stack exchange,提问作者Corey James Carney
相关产品推荐
相关产品推荐

