如何取消订阅ngrx / store?

时间:2017-11-26 12:58:25

标签: angular ngrx-store

我有一个组件可以从订阅商店获取数据。

this.store.select('somedata').subscribe((state: any) => {
  this.somedata = state.data;
});

我想在组件不存在的情况下取消订阅此订阅,在我订阅某些可观察对象的其他地方,如下所示:

this.service.data.subscribe(
   (result: any) => {//data}
);

我在ngOnOnDestroy上取消订阅,就像这样:

ngOnDestroy(){
   this.service.data.unsubscribe();
}

但是如果商店我无法做到,那就会给我错误:

Property 'unsubscribe' does not exist on type 'Store<State>'

5 个答案:

答案 0 :(得分:16)

订阅时,您将收到订阅对象,您可以调用unsubscribe()

const subscription = this.store.select('somedata').subscribe((state: any) => {
  this.somedata = state.data;
});
// later
subscription.unsubscribe();

ngOnInit(){
 this.someDataSubscription = this.store.select('somedata').subscribe((state: any) => {
  this.somedata = state.data;
 });
}

ngOnDestroy(){
  this.someDataSubscription.unsubscribe();
}

答案 1 :(得分:11)

有一个比最高投票答案更好的方法,您不必管理一堆订阅,而只需一个订阅。然后,您可以根据需要拥有任意数量的订阅,而不必创建大量不必要的var。

  public ngDestroyed$ = new Subject();

  public ngOnDestroy() {
    this.ngDestroyed$.next();
  }

  public ngOnInit() {
    this.myWhateverObs$
        .pipe(takeUntil(this.ngDestroyed$))
        .subscribe((val)=> { this.whatever()});
    this.myWhateverNPlusOne$
        .pipe(takeUntil(this.ngDestroyed$))
        .subscribe((val)=> { this.whatever()})
  }

答案 2 :(得分:5)

您可以在不直接调用subscribe方法的情况下获取值,通过异步管道获取值,如

@Component({
    template: `
        <div>Current Count: {{ counter | async }}</div>
    `
})
class MyAppComponent {
    counter: Observable<number>;

    constructor(private store: Store<AppState>){
        this.counter = store.select('counter');
    }
}

这里我们使用异步管道来获取价值。 异步管道订阅Observable或Promise并返回它发出的最新值。发出新值时,异步管道会标记要检查更改的组件。当组件被销毁时,异步管道会自动取消订阅以避免潜在的内存泄漏。

答案 3 :(得分:3)

我使用过的最简洁的方法是使用ngx-take-until-destroy库。 您的代码将如下所示:

this.store.select('somedata')
    .pipe(untilDestroyed(this))
    .subscribe((state: any) => {
        this.somedata = state.data;
    });

您还需要在类中使用ngOnDestroy()方法。

答案 4 :(得分:1)

此答案基于by FlavorScapeby mahyar提供的答案。

没有外部库解决方案

避免使用主题及其代码使每个组件component肿的一种方法是使用基本组件(已通过Angular 10.0.6测试):

base.component.ts

    function check_order_product_id( $order_id ){
  $order = wc_get_order( $order_id );
  $items = $order->get_items();
  $order_email = $order->billing_email;

  //create custom mail
  function get_custom_email_html( $order, $heading = false, $mailer ) {
    $template = 'emails/my-custom-email-i-want-to-send.php';
    return wc_get_template_html( $template, array(
        'order'         => $order,
        'email_heading' => $heading,
        'sent_to_admin' => false,
        'plain_text'    => false,
        'email'         => $mailer
    ) );
  }
  function send_custom_email($order_email, $order){
    // load the mailer class
    $mailer = WC()->mailer();
    //format the email
    $recipient = $order_email;
    $subject = __("Deine Login-Daten für Geomap", 'theme_name');
    $content = get_custom_email_html( $order, $subject, $mailer );
    $headers = "Content-Type: text/html\r\n";
    //send the email through wordpress
    $mailer->send( $recipient, $subject, $content, $headers );
  }

  function email_exists( $order_email ) {
    $user = get_user_by( 'email', $order_email );
    if ( $user ) {
      send_custom_email($order_email, $order);
//not sure where this variable was comming from
      return $wp_new_user_notification_email;
    }
    else {

      // random password with 12 chars
      $random_password = wp_generate_password();
      // create new user with email as username & newly created pw
      $user_id = wp_create_user( $order_email, $random_password, $order_email );
//not sure where this variable was comming from
      return $wp_new_user_notification_email;
    }
  }



  foreach ( $items as $item_id => $item ) {
    $product_id = $item->get_variation_id() ? $item->get_variation_id() : $item->get_product_id();
    if ( $product_id === XYZ ) {
      //get the user email from the order and check if registered already
      email_exists( $order_email );
    }
  }
}

foo.component.ts

import { Subject } from "rxjs";
import { Component } from "@angular/core";

@Component({
    selector: "app-base-component",
    template: ""
})
export class BaseComponent {
    public ngDestroyed$ = new Subject();

    public onDestroy(): void {
        this.ngDestroyed$.next();
    }
}

使用外部库

您可以使用@ngneat/until-destroy库来避免自定义代码,并支持其他方案(例如在服务内)

@Component({
    selector: "app-foo",
    templateUrl: "./foo.component.html",
    styleUrls: ["./foo.component.scss"]
})
export class FooComponent extends BaseComponent implements OnInit, OnDestroy {

    fooList$: Observable<FooModel[]>;

    @ViewChild(DataBindingDirective) dataBinding: DataBindingDirective;
    public gridData: any[];
    public gridView: any[];

    public mySelection: string[] = [];

    constructor(private readonly store: Store<AppState>) {
        super();
    }
    ngOnDestroy(): void {
        this.onDestroy();
    }

    ngOnInit(): void {
        this.store.dispatch(ApplicationFooItemsRequestedAction());
        this.fooList$ = this.store.select(selectAllApplicationFooItems);

        this.fooList$.pipe(takeUntil(this.ngDestroyed$)).subscribe(ul => {
            // do stuff with items
        });
    }
}