DoCheck wykrywanie zminan - Angular i ngDoCheck

W tym wpisie zrobię szybkie wprowadzenie do ngDoCkeck. Na początek dowiemy się czym jest ten angularowy hook i kiedy jest wywoływany, a także co takiego robi przy standardowym użyciu.

Do artykułu stworzyłem przykład: https://stackblitz.com/edit/angular-docheck-przyklad

Kiedy ngDoCheck jest uruchamiany?

Hook ngDoCheck w czasie tworzenia komponentu jest uruchamiany jako trzeci w kolejności, jeśli chodzi o lifecycle hook Angulara. Najpierw ngOnChanges potem ngOnInit a na końcu ngDoCheck. Sam konstruktor jak pamiętamy uruchamia się zawsze jako pierwszy, ale nie należy on do lifecycle hook.

Dodatkowo uruchamia się za każdym razem gdy uruchomi się Change Detection w Angularze. Może więc uruchomić się w naszym komponencie wielokrotnie. Mało tego, jeżeli coś wydarzy się w naszym komponencie, hook ten uruchamiany jest także we wszystkich child komponentach.

Co robi ngDoCheck?

Ja już wiemy ngDoCheck uruchamia się za każdym razem gdy uruchomi się Change Detection. Dlatego hook ten może nam posłużyć do zaimplementowania własnego wykrywania zmian.

Aby móc posługiwać się ngDoCheck musimy implementować interfejs DoCheck:

export class ChildComponent DoCheck {

 // Uruchomi się zawsze z Change Detection
 ngDoCheck(): void {
    console.log('Uruchamiam się zawsze :)')
 }
}

Generalnie Change Detection w Angularze działa przez porównywanie referencji jeśli chodzi o obiekty i listy. Jeżeli będziemy mieli w komponencie pole @Input, które reprezentowane jest przez obiekt, możemy mieć problem z wykryciem zmian w takim obiekcie. Jeżeli oczywiście chcemy kontrolować zmiany.

export class ChildComponent DoCheck {
 @Input() dog: Dog;

 // Uruchomi się zawsze z Change Detection
 ngDoCheck(): void {
   console.log('Uruchamiam się zawsze :)')
 }
}

Do wykrywania zmian w polach @Input służy ngOnChanges i tam moglibyśmy oczekiwać na zmiany obiektu. Jeżeli jednak nadrzędny komponent spróbuje zmienić obiekt w ten sposób:

 change() {
   this.dog.name = 'Pluto';
 }

Change Detection w Angularze porówna tylko referencję i stwierdzi, że to ten sam obiekt. W tej sytuacji zmienia się tylko pole obiektu, więc Angular traktuje to jako ten sam obiekt i metoda ngOnChanges nie uruchomi się.

Dopiero stworzenie obiektu od nowa, z nową wartością i ponowne jego przekazanie przez @Input sprawi, że metoda ngOnChanges odnotuje tą zmianę. W tym przypadku Change Detection zauważy, że zmienił się obiekt (czyli zmieniła się referencja).

Taki zapis już zadziała:

 change() {
   this.dog = {name: 'Pluto'};
 }

Ponieważ ngDoCheck uruchamia się przy każdym uruchomieniu Change Detection to możemy stworzyć własny change detection, który sprawdzi dokładnie każde pole w obiekcie. Prosta implementacja może wyglądać tak:

  ngDoCheck(): void {
    console.log('Sprawdzam!');
    if (this.dogCopy.name !== this.dog.name) {
      console.log(`Nastąpiła zmiana z ${this.dogCopy.name} na ${this.dog.name}`);
      this.dogCopy = {...this.dog};
    }
  }

W tym przykładzie musimy trzymać w komponencie dodatkowo kopię obiektu, który chcemy sprawdzać. W taki prosty sposób możemy tworzyć własny change detection w metodzie ngDoCheck.

Na co uważać przy ngDoChek

Z metodą ngDoCheck należy uważać i unikać jakichkolwiek skomplikowanych operacji. Metoda ta może wywoływać się wielokrotnie. Dodatkowo, ngDoCheck uruchamia się w każdym komponencie położonym niżej. Przy dużych aplikacjach i częstym stosowaniu ngDoCheck , a także wielu eventach, możemy narobić sobie więcej kłopotu niż pożytku.

Wystarczy że użyjemy eventu mousemove w naszym komponencie i metoda będzie wywoływana przy każdym poruszeniu myszki. Dlatego trzeba podchodzić rozważnie do używania tej metody.

Nie używamy ngDoCheck razem z ngOnChanges. Ponieważ może się zdarzyć, że będziemy reagowali dwa razy na jedną zmianę. Dlatego implementację taką trzeba dobrze przemyśleć i albo używać jednej albo drugiej metody.

Gdzie jeszcze używać ngDoCheck

Mój przykład to prosty przykład sprawdzania zmian w obiekcie. Możemy użyć specjalnej klasy KeyValueDiffers . Opiszę to w innym artykule.

Metoda ngDoCheck przydaje się także gdy przestawimy Change Detection w Angularze na ChangeDetectionStrategy.OnPush. Na to także będzie inny wpis.

Wnioski

  • ngDoCheck uruchamia się zawsze z Change Detection
  • możemy go używać do kontrolowania każdej zmiany w komponencie
  • w porównaniu do ngOnChanges, który nie uruchamia się zawsze
  • dzięki ngDoCheck możemy dowiedzieć się, że nastąpiła jakaś zmiana w komponencie tworząc własny dodatkowy change detection
  • możemy regować na zmiany w polach @Input, których nie wykrywa metoda ngOnChanges
  • ngDoCheck używajmy roztropnie ponieważ może uruchamiać się w komponencie setki razy
  • przedstawiłem najprostsze ngDoCheck i jeszcze będę o nim pisał

Linki

Mój przykład na StackBlitz: https://stackblitz.com/edit/angular-docheck-przyklad

https://medium.com/angular-in-depth/if-you-think-ngdocheck-means-your-component-is-being-checked-read-this-article-36ce63a3f3e5

https://angular.io/api/core/DoCheck