【问题标题】:Angular ngrx store subscribe to state called multiple timesAngular ngrx 商店订阅多次调用的状态
【发布时间】:2022-10-21 19:37:28
【问题描述】:

我正在使用 ngRxStore 处理 Angular 13 项目,在我的组件中,我有以下代码:

  enterpriseState$: Observable<EnterpriseState> | null = null;
  exporter$: Observable<ExporterDTO> | null = null;

  readonly exporterStateEnum = EnterpriseStateEnum;

  ngOnInit(): void {
    this.enterpriseState$ = this.store.pipe(
      map((state) => state.myStateModule.enterpriseState)
    );

    if (this.enterpriseState$) {

      this.enterpriseState$.subscribe((state: EnterpriseState) => {
        if (state && state.dataState === EnterpriseStateEnum.LOADED) {
            
          console.log(' subscribe enterpriseeeee LOADED'); // this log is called 5 times
          
          // And this http request is called 5 times too
          this.exporter$ = this.myService.getEnterprise(state.exporter?.identifier).pipe(shareReplay(1));

        }

      });


    }
    
  }
  //this is my stated object
  export interface EnterpriseState {
    exporter: EnterpriseDTO,
    errorMessage: string,
    dataState: EnterpriseStateEnum
}

问题是我的订阅被调用了 5 次,并且多次触发了 http 请求。

你知道我的代码有什么问题以及如何避免多次调用。 我已经尝试过运营商skip and take,但它没有用。

【问题讨论】:

  • 是的,你有内存泄漏。试试这个:this.enterpriseState$ = this.store.pipe(first(), map((state) =&gt; state.myStateModule.enterpriseState));。您还可以删除您的 if 检查,因为它是多余的,并且只会添加额外的嵌套级别 if (this.enterpriseState$)
  • 如果您的组件处理此enterpriseState 的多个状态更改,并且它必须在其生命周期内对这些更改做出反应,那么实现OnDestroy 并取消订阅连接到ngrx 状态的所有可观察对象。
  • 然后删除 first 并将订阅的引用存储为组件中的私有字段。在组件中实现OnDestroy 接口并在ngOnDestroy 生命周期挂钩内取消订阅。
  • 当您使用 map 运算符时,即使您的“映射值”没有更改,您的 observable 也可能会发出。因此,您应该使用store.select() 而不是store.pipe(map(...))。在您当前的代码中,尝试在 map 运算符后添加 tap(val =&gt; console.log(val)) 并查看是否重复发出相同的值,或者每次的值是否不同。如果值相同,切换到store.select() 可能会解决您的问题。
  • @BizzyBob 非常感谢您的建议,我会尝试并与您保持联系

标签: angular rxjs ngrx


【解决方案1】:

每当您打印/初始化您的组件时,您将获得另一个订阅(没有上限),最终会减慢您的应用程序。

您需要做的是在销毁组件时取消订阅。

试试看:

     import { Component, OnDestroy, OnInit } from "@angular/core";
       export class YourComponent implements OnInit, OnDestroy
    
    //you need a field to store your subscriptions    
    private subscriptions: Subscription[];
     
    //initialize it in constructor
    constructor(private store: Store<AppState>) {
            this.subscriptions = [];
    }
    
//now when you use it, just push it into the array
    ngOnInit(): void {
        this.enterpriseState$ = this.store.pipe(
          map((state) => state.myStateModule.enterpriseState)
        );
    
        if (this.enterpriseState$) {
    
        //here it is...
          this.subscriptions.push(this.enterpriseState$.subscribe((state: EnterpriseState) => {
            if (state && state.dataState === EnterpriseStateEnum.LOADED) {
                
              console.log(' subscribe enterpriseeeee LOADED'); // this log is called 5 times
              
              // And this http request is called 5 times too
              this.exporter$ = this.myService.getEnterprise(state.exporter?.identifier).pipe(shareReplay(1));
    
            }
    
          }));
    
    
        }
        
      }

这是缺失的部分:

ngOnDestroy() {
        this.subscriptions.forEach(sub => sub.unsubscribe());
    }

而已。您不应再在任何地方订阅多个。

【讨论】:

    猜你喜欢
    • 2019-03-06
    • 2018-02-16
    • 1970-01-01
    • 1970-01-01
    • 2019-08-18
    • 1970-01-01
    • 2020-03-26
    • 2019-02-12
    • 1970-01-01
    相关资源
    最近更新 更多