programing

RxJS: TakeUntil() Angular 구성 요소의 ngOnDestroy()

powerit 2023. 8. 1. 20:51
반응형

RxJS: TakeUntil() Angular 구성 요소의 ngOnDestroy()

tl;dr: 기본적으로 저는 앵귤러와 결혼하고 싶습니다.ngOnDestroy Rxjs 함께를 takeUntil()교환. -- 그게 가능한가요?

여러 Rxjs 구독을 여는 Angular 구성 요소가 있습니다.구성 요소가 파괴되면 이러한 구성 요소를 닫아야 합니다.

이에 대한 간단한 해결책은 다음과 같습니다.

class myComponent {

  private subscriptionA;
  private subscriptionB;
  private subscriptionC;

  constructor(
    private serviceA: ServiceA,
    private serviceB: ServiceB,
    private serviceC: ServiceC) {}

  ngOnInit() {
    this.subscriptionA = this.serviceA.subscribe(...);
    this.subscriptionB = this.serviceB.subscribe(...);
    this.subscriptionC = this.serviceC.subscribe(...);
  }

  ngOnDestroy() {
    this.subscriptionA.unsubscribe();
    this.subscriptionB.unsubscribe();
    this.subscriptionC.unsubscribe();
  }

}

이것은 효과가 있지만, 약간 중복됩니다. 않습니다 - 나는특그좋않지습하다니그아것 - 히을그.unsubscribe()- 다른 곳에 있습니다. - 구성 요소 상태가 구독으로 오염되었습니다.

▁the다▁prefer를 사용하는 것을 더 합니다.takeUntil()연산자 또는 이와 유사한 것을 사용하여 다음과 같이 표시합니다.

class myComponent {

  constructor(
    private serviceA: ServiceA,
    private serviceB: ServiceB,
    private serviceC: ServiceC) {}

  ngOnInit() {
    const destroy = Observable.fromEvent(???).first();
    this.subscriptionA = this.serviceA.subscribe(...).takeUntil(destroy);
    this.subscriptionB = this.serviceB.subscribe(...).takeUntil(destroy);
    this.subscriptionC = this.serviceC.subscribe(...).takeUntil(destroy);
  }

}

내가 사용할 수 있는 파괴 이벤트나 비슷한 것이 있습니까?takeUntil()구성 요소 아키텍처를 단순화하는 또 다른 방법은 무엇입니까?도 있고, 내에서 는 이벤트를 만들 수도 것을 .ngOnDestroy()하지만 그것은 결국 책을 읽는 것을 그렇게 쉽게 만들지는 못할 것입니다.

▁a▁를 활용할 수 있습니다.ReplaySubject이를 위해:

편집: RxJS 6.x 이후로 다름: RxJS 6.x의 사용에 유의하십시오.pipe()방법.

class myComponent {
  private destroyed$: ReplaySubject<boolean> = new ReplaySubject(1);

  constructor(
    private serviceA: ServiceA,
    private serviceB: ServiceB,
    private serviceC: ServiceC) {}

  ngOnInit() {
    this.serviceA
      .pipe(takeUntil(this.destroyed$))
      .subscribe(...);
    this.serviceB
      .pipe(takeUntil(this.destroyed$))
      .subscribe(...);
    this.serviceC
      .pipe(takeUntil(this.destroyed$))
      .subscribe(...);
  }

  ngOnDestroy() {
    this.destroyed$.next(true);
    this.destroyed$.complete();
  }
}

이것은 RxJS 5.x 이하에서만 유효합니다.

class myComponentOld {
  private destroyed$: ReplaySubject<boolean> = new ReplaySubject(1);

  constructor(private serviceA: ServiceA) {}

  ngOnInit() {
    this.serviceA
      .takeUntil(this.destroyed$)
      .subscribe(...);
  }

  ngOnDestroy() {
    this.destroyed$.next(true);
    this.destroyed$.complete();
  }
}

componentDestroyed()npm 패키지의 함수 @w11k/ngx 구성 요소가 파괴되는 것이 takeAtil:

@Component({
  selector: 'foo',
  templateUrl: './foo.component.html'
})
export class FooComponent implements OnInit, OnDestroy {
  ngOnInit() {
    Observable.interval(1000)
      .takeUntil(componentDestroyed(this)) // <--- magic is here!
      .subscribe(console.log);
  }

  ngOnDestroy() {}
}

의 버전은 다음과 같습니다.componentDestroyed()코드에 직접 포함할 수 있습니다.

// Based on https://www.npmjs.com/package/ng2-rx-componentdestroyed
import { OnDestroy } from '@angular/core';
import { ReplaySubject } from 'rxjs/ReplaySubject';

export function componentDestroyed(component: OnDestroy) {
  const oldNgOnDestroy = component.ngOnDestroy;
  const destroyed$ = new ReplaySubject<void>(1);
  component.ngOnDestroy = () => {
    oldNgOnDestroy.apply(component);
    destroyed$.next(undefined);
    destroyed$.complete();
  };
  return destroyed$;
}

이것은 구독을 종료한다는 의미로 귀결됩니다.이 작업에는 기본적으로 두 가지 방법이 방법은 다음과 같습니다.

  1. 예:takeWhile()).
  2. 소스 관찰 가능에서 구독을 취소합니다.

이 두 가지가 같지 않다는 것을 알게 되어 기쁩니다.

를 들어 를 는 를들어사는경우하용예우경▁when.takeWhile()에게 당신은자운가보합니다록도내영합▁the▁send를 보내도록 합니다.complete사용자의 관찰자에게 전파되는 알림입니다.그래서 정의하면 다음과 같습니다.

...
.subscribe(..., ..., () => doWhatever());

그리고 나서 당신이 eg로 체인을 완성할 때.takeWhile()그자리의 doWhatever()함수가 호출됩니다.

예를 들어, 다음과 같이 보일 수 있습니다.

const Observable = Rx.Observable;
const Subject = Rx.Subject;

let source = Observable.timer(0, 1000);
let subject = new Subject();

source.takeUntil(subject).subscribe(null, null, () => console.log('complete 1'));
source.takeUntil(subject).subscribe(null, null, () => console.log('complete 2'));
source.takeUntil(subject).subscribe(null, null, () => console.log('complete 3'));

setTimeout(() => {
  subject.next();
}, 3000);

3초 후에 모든 콜백이 호출됩니다.

반면 구독을 취소하면 소스 Observable에서 생성한 항목에 더 이상 관심이 없다는 것을 의미합니다.그러나 이것은 소스가 완료되어야 한다는 것을 의미하지 않습니다.당신은 더 이상 신경 쓰지 않아요.

이 모든 즉, 수 합니다.Subscription에서 s..subscribe(...)전화를 걸어 한 번에 모든 전화를 수신 취소합니다.

let subscriptions = new Rx.Subscription();
let source = Observable.timer(0, 1000);

subscriptions.add(source.subscribe(null, null, () => console.log('complete 1')));
subscriptions.add(source.subscribe(null, null, () => console.log('complete 2')));
subscriptions.add(source.subscribe(null, null, () => console.log('complete 3')));

setTimeout(() => {
  subscriptions.unsubscribe();
}, 3000);

이제 3초 지연 후에는 구독을 취소하고 완전한 콜백이 실행되지 않았기 때문에 콘솔에 아무것도 인쇄되지 않습니다.

따라서 사용하고자 하는 것은 사용자와 사용 사례에 달려 있습니다.구독을 취소하는 것은 완료하는 것과 같지 않다는 것을 알아두십시오. 비록 당신의 상황에서는 그렇게 중요하지 않다고 생각하지만요.

TakeUntil (2022년 4월 13일)과 함께 다형성을 사용하십시오.

글을 쓰는 경우protected destroy$ = new Subject<void>();여러분이 만드는 모든 구성 요소에서 여러분은 스스로에게 "왜 저는 DRY(반복하지 않는) 원칙을 따르지 않습니까?"라고 물어야 합니다.

DRY 원칙을 따르려면 파괴 신호를 처리하는 추상 기본 구성 요소(추상 클래스는 직접 인스턴스화할 수 없음)를 만듭니다.

@Component({ template: '' })
export abstract class BaseComponent extends Subscribable {
  // Don't let the outside world trigger this destroy signal.
  // It's only meant to be trigger by the component when destroyed! 
  private _destroy = new Subject<void>();
  public destroy$ = this._destroy as Observable<void>;
  /** Lifecycle hook called by angular framework when extended class dies. */
  ngOnDestroy(): void {
    this._destroy.next();
  }
}

편리한 확장 기능을 만들어 업무를 단순화합니다.

declare module 'rxjs/internal/Observable' {
  interface Observable<T> {
    dieWith(comp: BaseComponent): Observable<T>;
  }
}

Observable.prototype.dieWith = function<T>(comp: BaseComponent): Observable<T> {
    return this.pipe(takeUntil(comp.destroy$));
};

구독을 처리해야 할 때마다 기본 구성요소를 확장합니다.

@Component({ ... })
export class myComponent extends BaseComponent {

  constructor(
    private serviceA: ServiceA,
    private serviceB: ServiceB,
    private serviceC: ServiceC
  ) {
    super();
  }

  ngOnInit() {
    this.serviceA.a$.dieWith(this).subscribe(...);
    this.serviceB.b$.dieWith(this).subscribe(...);
    this.serviceC.c$.dieWith(this).subscribe(...);
  }

}

Angular Components에서 정식으로 프로처럼 서브스크립션을 처리했습니다.

여러분의 동료들은 나중에 여러분에게 감사할 것입니다!

해피 코딩!

구성 요소가 경로에 직접 연결되어 있는 경우 이벤트를 활용하여 상태를 추가하지 않도록 할 수 있습니다.이렇게 하면 구성 요소에서 벗어나 탐색하는 즉시 해당 구성 요소의 헤드라인 등록이 자동으로 정리됩니다.

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { MyService } from './my.service';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/takeUntil';

@Component({
    ...
})
export class ExampleComopnent implements OnInit {

    constructor(private router: Router, private myService: MyService) {
    }

    ngOnInit() {
        this.myService.methodA()
            .takeUntil(this.router.events)
            .subscribe(dataA => {
                ...
            });

        this.myService.methodB()
            .takeUntil(this.router.events)
            .subscribe(dataB => {
                ...
            });
    }
}

참고: 이 간단한 예에서는 보호된 경로나 취소된 경로 탐색을 고려하지 않습니다.라우터 이벤트하나가 트리거될 가능성이 있지만 경로 탐색이 취소되는 경우, 라우터 이벤트를 필터링하여 적절한 지점(: 경로 보호 확인 후 또는 탐색이 완료된 후)에서 트리거되도록 해야 합니다.

this.myService.methodA()
    .takeUntil(this.router.events.filter(e => e instanceOf NavigationEnd))
    .subscribe(dataA => {
        ...
    });

기본 클래스 만들기

import { Subject } from 'rxjs/Rx';
import { OnDestroy } from '@angular/core';

 export abstract class Base implements OnDestroy {

 protected componentDestroyed$: Subject<any>;

constructor() {
    this.componentDestroyed$ = new Subject<void>();

    const destroyFunc = this.ngOnDestroy;
    this.ngOnDestroy = () => {
        destroyFunc.bind(this)();
        this.componentDestroyed$.next();
        this.componentDestroyed$.complete();
    };
}
// placeholder of ngOnDestroy. no need to do super() call of extended class.
public ngOnDestroy() {
    // no-op
}

}

구성 요소는 다음과 같습니다.

기본 클래스를 확장합니다.

export class Test extends Base{
}

정기구독 중에

service.takeUntil(this.componentDestroyed$
    .subscribe(...)

이는 글로벌 수준 변경이며, 구독을 원할 때마다 프로젝트 전체에서 동일한 접근 방식을 사용합니다.기본 클래스에서 필요한 모든 변경 사항을 수정할 수 있습니다.

Angular 16은 생성자에서 그렇게 사용할 수 있는 새로운 takeUntilDestroy 함수를 제공합니다.

import { Component } from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";

@Component({
  selector: "my-component",
  templateUrl: "./my-component.html",
  styleUrls: ["./my-component.scss"]
})
export class MyComponent {

  constructor(private http: HttpClient) {
     this.http.get('/api')
       .pipe(takeUntilDestoryed())
       .subscribe();
  }
}

참고 생성자 외부에서 동일한 작업을 수행하려는 경우 이 오류가 나타날 수 있습니다.takeUntilDestroyed() can only be used within an injection context such as a constructor과 같이 을(를) 참조하십시오.

import { Component, DestroyRef, OnInit, inject } from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";

@Component({
  selector: "my-component",
  templateUrl: "./my-component.html",
  styleUrls: ["./my-component.scss"]
})
export class MyComponent implements OnInit {
  destroyRef = inject(DestroyRef);

  ngOnInit(): void {
     this.http.get('/api')
       .pipe(takeUntilDestoryed(this.destroyedRef))
       .subscribe();
  }
}

2023년 5월이며 Angular 팀은 임박한 Angular 16에 대한 시나리오를 처리하기 위해 takeUntilDestroyed()를 구현했습니다. https://github.com/angular/angular/search?q=takeUntilDestroyed

Angular 16 이후 리소스를 자동으로 구독 취소 및 해제하는 작업을 수행하는 새로운 연산자가 있습니다. takeUntilDestored

예:

import { takeUntilDestoryed } from 'angular/core/rxjs-interop';

@Component({...})
export class AppComponent implements OnInit {

  destroyRef = inject(DestroyRef);
  
  constructor(private http: HttpClient) {}

  public ngOnInit(): void {
    this.http.get('/api').pipe(takeUntilDestoryed(this.destroyRef)).subscribe();
  }
}

문서: https://angular.io/api/core/rxjs-interop/takeUntilDestroyed

언급URL : https://stackoverflow.com/questions/42490265/rxjs-takeuntil-angular-components-ngondestroy

반응형