Lifecycle Hooks

5 questions found

What are lifecycle hooks in Angular and why would you use them?

Beginner
Lifecycle hooks are special methods Angular calls automatically at specific points in a component's life, such as right after it is created or right before it is destroyed, letting you run your own code exactly when needed, like fetching data or cleaning up a resource.
import { OnInit } from '@angular/core';

@Component({ selector: 'app-profile' })
export class ProfileComponent implements OnInit {
  ngOnInit() {
    console.log('Component has been initialized');
  }
}
Real-world example A profile component fetches the user's data as soon as it is created, using the ngOnInit lifecycle hook to trigger that initial data load exactly once.

Common follow-ups: Why is ngOnInit generally preferred over the constructor for fetching data?;What other common lifecycle hooks does Angular provide besides ngOnInit?

Components & Templates;Services & Dependency Injection

What does ngOnDestroy do, and why is it important for cleaning up resources like subscriptions?

Intermediate
ngOnDestroy runs right before Angular removes a component from the page, giving you a chance to clean up anything that would otherwise keep running unnecessarily, such as unsubscribing from an observable or clearing a timer, preventing memory leaks in a long running application.
import { OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs';

export class TimerComponent implements OnDestroy {
  private subscription: Subscription;

  constructor() {
    this.subscription = interval(1000).subscribe(() => console.log('tick'));
  }

  ngOnDestroy() {
    this.subscription.unsubscribe();
  }
}
Real-world example A live clock component correctly stops its timer when the user navigates away from the page, because it unsubscribes inside ngOnDestroy, avoiding a hidden timer that keeps running forever in the background.

Common follow-ups: What happens if you forget to unsubscribe from an observable inside ngOnDestroy?;How does the async pipe help avoid needing to manually unsubscribe in many cases?

RxJS & Observables;Performance & Lazy Loading

What is the difference between ngOnChanges and ngOnInit, and when does each run?

Intermediate
ngOnChanges runs whenever one of a component's input properties changes, including the very first time it receives a value, and it receives an object describing exactly what changed. ngOnInit runs exactly once, right after the very first ngOnChanges call, making it the ideal place for one time setup logic rather than logic that needs to react to every future change.
import { OnChanges, SimpleChanges } from '@angular/core';

export class UserCardComponent implements OnChanges {
  @Input() userId!: number;

  ngOnChanges(changes: SimpleChanges) {
    if (changes['userId']) {
      console.log('userId changed to', changes['userId'].currentValue);
    }
  }
}
Real-world example A user profile component reloads its data every time the userId input changes, using ngOnChanges to detect exactly when that specific input updates, rather than only running once when the component first appears.

Common follow-ups: What is inside the SimpleChanges object passed to ngOnChanges?;Would using a signal input instead of a traditional Input make ngOnChanges unnecessary here?

Angular Signals;Data Binding

What is the correct order in which Angular calls the main lifecycle hooks during a component's life?

Advanced
Angular calls them in a predictable sequence: ngOnChanges first if there are any inputs, then ngOnInit once, followed by ngDoCheck, ngAfterContentInit, ngAfterContentChecked, ngAfterViewInit, and ngAfterViewChecked, with ngOnChanges, ngDoCheck, and the checked hooks all potentially running again on subsequent updates, and ngOnDestroy running once at the very end.
ngOnChanges() { console.log('1. Inputs changed'); }
ngOnInit() { console.log('2. Component initialized'); }
ngAfterViewInit() { console.log('3. View fully initialized'); }
ngOnDestroy() { console.log('4. Component about to be destroyed'); }
Real-world example A developer debugging why a child element referenced with ViewChild was undefined discovers it is only available starting in ngAfterViewInit, not in the earlier ngOnInit, since the view has not fully rendered yet at that point.

Common follow-ups: Why is ViewChild data only reliably available starting in ngAfterViewInit?;What is the practical difference between ngAfterContentInit and ngAfterViewInit?

Template Reference Variables & ViewChild;Content Projection (ng-content)

Why is the constructor generally not the right place to fetch data or perform complex setup in an Angular component?

Beginner
The constructor's main job is simply setting up dependency injection, and at that point Angular has not yet fully set up the component's inputs or view. Fetching data or performing complex setup logic belongs in ngOnInit instead, which runs once Angular has properly initialized the component and its inputs are actually available.
// Not recommended, inputs may not be reliably set yet
constructor() {
  console.log(this.userId); // could be undefined here
}

// Correct, inputs are guaranteed to be set by this point
ngOnInit() {
  console.log(this.userId); // reliably available
}
Real-world example A developer fixes a confusing bug where a component's data fetch used an undefined id, by moving the fetch call from the constructor into ngOnInit, where the required input value is reliably available.

Common follow-ups: What kinds of things are actually appropriate to do inside a constructor in Angular?;How does this same guidance apply differently for signal based inputs?

Dependency Injection Providers & Injection Tokens;Angular Signals