# Component Lifecycle Hook

Course: [Angular](https://www.devtutorials.ai/learn/angular) · Section: Core Angular Concepts
Source: https://www.devtutorials.ai/learn/angular/core-concepts/lifecycle

> The stages of a component's life and the hooks Angular calls at each one, from ngOnChanges and ngOnInit to the content and view hooks and ngOnDestroy, with best practices.

## Introduction to Component Lifecycle

The **component lifecycle** is the sequence of stages a component goes through: Angular creates it, sets its inputs, renders its view, checks it for changes, and finally destroys it.

At each stage, Angular calls a **lifecycle hook**: a method with a fixed name, such as `ngOnInit` or `ngOnDestroy`. If your component class defines that method, Angular runs your code at that moment.

### Real-World Example

A live stock-price widget must do different work at different moments. When it first appears, it loads the current prices. When the user switches to another market, it reloads them. Once its template is on the screen, it places the cursor in the search box.

When the user leaves the page, the widget must close its live connection and stop its refresh timer. Otherwise the app keeps sending network requests for a widget nobody can see. Lifecycle hooks give you a method for each of these moments.

## Angular Lifecycle Hooks Overview

<figure className="ngc-fig">
<svg viewBox="0 0 640 150" role="img" aria-label="Lifecycle order: ngOnChanges, ngOnInit, ngDoCheck, the content hooks, the view hooks, then ngOnDestroy. Change detection repeats the check hooks.">
<defs><marker id="ngc-e3" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0 0L10 5L0 10z" className="ngc-arrowhead" /></marker></defs><g className="ngc-step ngc-t1"><g className="ngc-node ngc-soft c-purple"><rect x="6" y="36" width="96" height="48" rx="9" /><text x="54.0" y="64" textAnchor="middle" style={{fontSize:"12px"}}>ngOnChanges</text></g></g><line className="ngc-link c-slate" x1="104" y1="60" x2="110" y2="60" markerEnd="url(#ngc-e3)" /><g className="ngc-step ngc-t2"><g className="ngc-node ngc-soft c-blue"><rect x="112" y="36" width="96" height="48" rx="9" /><text x="160.0" y="64" textAnchor="middle" style={{fontSize:"12px"}}>ngOnInit</text></g></g><line className="ngc-link c-slate" x1="210" y1="60" x2="216" y2="60" markerEnd="url(#ngc-e3)" /><g className="ngc-step ngc-t3"><g className="ngc-node ngc-soft c-blue"><rect x="218" y="36" width="96" height="48" rx="9" /><text x="266.0" y="64" textAnchor="middle" style={{fontSize:"12px"}}>ngDoCheck</text></g></g><line className="ngc-link c-slate" x1="316" y1="60" x2="322" y2="60" markerEnd="url(#ngc-e3)" /><g className="ngc-step ngc-t4"><g className="ngc-node ngc-soft c-green"><rect x="324" y="36" width="96" height="48" rx="9" /><text x="372.0" y="64" textAnchor="middle" style={{fontSize:"12px"}}>AfterContent</text></g></g><line className="ngc-link c-slate" x1="422" y1="60" x2="428" y2="60" markerEnd="url(#ngc-e3)" /><g className="ngc-step ngc-t4"><g className="ngc-node ngc-soft c-green"><rect x="430" y="36" width="96" height="48" rx="9" /><text x="478.0" y="64" textAnchor="middle" style={{fontSize:"12px"}}>AfterView</text></g></g><line className="ngc-link c-slate" x1="528" y1="60" x2="534" y2="60" markerEnd="url(#ngc-e3)" /><g className="ngc-step ngc-t4"><g className="ngc-node ngc-solid c-red"><rect x="536" y="36" width="96" height="48" rx="9" /><text x="584.0" y="64" textAnchor="middle" style={{fontSize:"12px"}}>ngOnDestroy</text></g></g><path className="ngc-link ngc-flow c-orange" d="M440 88 C 420 128, 140 128, 120 88" markerEnd="url(#ngc-e3)" style={{fill:"none"}} /><text className="ngc-note" x="280" y="138" textAnchor="middle">Every change: ngOnChanges (if inputs changed), ngDoCheck, the Checked hooks</text><text className="ngc-note" x="54" y="108" textAnchor="middle">created</text><text className="ngc-note" x="587" y="108" textAnchor="middle">removed</text>
</svg>
<figcaption>Creation runs left to right once. Each change detection run repeats the check hooks.</figcaption>
</figure>

Angular calls the hooks in this order:

| Order | Hook | When Angular calls it | Runs |
|---|---|---|---|
| 1 | `ngOnChanges` | An input value is set or changes | Before `ngOnInit`, then on every input change |
| 2 | `ngOnInit` | After the first inputs are set | Once |
| 3 | `ngDoCheck` | During every change detection run | Many times |
| 4 | `ngAfterContentInit` | After projected content is placed | Once |
| 5 | `ngAfterContentChecked` | After projected content is checked | Many times |
| 6 | `ngAfterViewInit` | After the component's view and child views are created | Once |
| 7 | `ngAfterViewChecked` | After the view is checked | Many times |
| 8 | `ngOnDestroy` | Just before Angular removes the component | Once |

Each hook has a matching TypeScript interface, such as `OnInit` or `OnDestroy`. Writing `implements OnInit` is optional, but it makes the compiler check the method name.

```ts
import { Component, OnDestroy, OnInit } from '@angular/core';

@Component({ selector: 'app-clock', template: `...` })
export class Clock implements OnInit, OnDestroy {
  ngOnInit() { /* start */ }
  ngOnDestroy() { /* clean up */ }
}
```

Most components need only two hooks: `ngOnInit` to start work and `ngOnDestroy` to clean up. Use the other hooks when you have a specific reason.

The next block logs every hook as it runs. Click the buttons and read the log.

```angular runnable title="Every lifecycle hook, logged in the order Angular calls it"
import {
  AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, Component, DoCheck,
  Input, OnChanges, OnDestroy, OnInit, SimpleChanges, signal,
} from '@angular/core';

const log: string[] = [];

@Component({
  selector: 'app-tracker',
  template: `<p>Tracker shows: {{ value }} <ng-content /></p>`,
})
export class Tracker implements OnChanges, OnInit, DoCheck, AfterContentInit,
  AfterContentChecked, AfterViewInit, AfterViewChecked, OnDestroy {
  @Input() value = 0;
  checks = 0;

  ngOnChanges(changes: SimpleChanges) { log.push('ngOnChanges: value = ' + changes['value'].currentValue); }
  ngOnInit() { log.push('ngOnInit'); }
  ngDoCheck() { this.checks++; }
  ngAfterContentInit() { log.push('ngAfterContentInit'); }
  ngAfterContentChecked() {}
  ngAfterViewInit() { log.push('ngAfterViewInit'); }
  ngAfterViewChecked() {}
  ngOnDestroy() { log.push('ngOnDestroy'); }
}

@Component({
  selector: 'app-root',
  imports: [Tracker],
  template: `
    <button (click)="value = value + 1; refresh()">Change input</button>
    <button (click)="show = !show; refresh()">{{ show ? 'Destroy' : 'Create' }} tracker</button>
    @if (show) {
      <app-tracker [value]="value">(projected text)</app-tracker>
    }
    <ol>
      @for (line of lines(); track $index) { <li>{{ line }}</li> }
    </ol>
  `,
})
export class App implements AfterViewInit {
  value = 1;
  show = true;
  lines = signal<string[]>([]);

  // The hooks run while Angular updates the page, so the log is copied
  // into a signal just after that update finishes.
  ngAfterViewInit() {
    this.refresh();
  }

  refresh() {
    setTimeout(() => this.lines.set([...log]));
  }
}
```

The first run logs `ngOnChanges`, `ngOnInit`, `ngAfterContentInit` and `ngAfterViewInit`, in that order. **Change input** adds another `ngOnChanges`. **Destroy tracker** adds `ngOnDestroy`.

## `ngOnChanges`

`ngOnChanges` runs whenever Angular sets or changes an **input** of the component. It runs once before `ngOnInit`, and again each time the parent passes a new value.

<figure className="ngc-fig">
<svg viewBox="0 0 640 134" role="img" aria-label="An input changes from Asha to Ravi; Angular calls ngOnChanges with previousValue and currentValue.">
<defs><marker id="ngc-l1" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0 0L10 5L0 10z" className="ngc-arrowhead" /></marker></defs><g className="ngc-step ngc-t1"><g className="ngc-node ngc-solid c-blue"><rect x="6" y="30" width="145" height="64" rx="9" /><text x="78.5" y="59" textAnchor="middle" style={{fontSize:"14px"}}>Parent sets [name]</text><text className="ngc-sub" x="78.5" y="77" textAnchor="middle">"Asha" → "Ravi"</text></g></g><line className="ngc-link ngc-flow c-slate" x1="152" y1="62" x2="166" y2="62" markerEnd="url(#ngc-l1)" /><g className="ngc-step ngc-t2"><g className="ngc-node ngc-soft c-orange"><rect x="167" y="30" width="145" height="64" rx="9" /><text x="239.5" y="59" textAnchor="middle" style={{fontSize:"14px"}}>Angular</text><text className="ngc-sub" x="239.5" y="77" textAnchor="middle">sees a new input</text></g></g><line className="ngc-link ngc-flow c-slate" x1="313" y1="62" x2="327" y2="62" markerEnd="url(#ngc-l1)" /><g className="ngc-step ngc-t3"><g className="ngc-node ngc-soft c-purple"><rect x="328" y="30" width="145" height="64" rx="9" /><text x="400.5" y="59" textAnchor="middle" style={{fontSize:"14px"}}>ngOnChanges()</text><text className="ngc-sub" x="400.5" y="77" textAnchor="middle">gets the changes</text></g></g><line className="ngc-link ngc-flow c-slate" x1="474" y1="62" x2="488" y2="62" markerEnd="url(#ngc-l1)" /><g className="ngc-step ngc-t4"><g className="ngc-node ngc-soft c-green"><rect x="489" y="30" width="145" height="64" rx="9" /><text x="561.5" y="59" textAnchor="middle" style={{fontSize:"14px"}}>changes.name</text><text className="ngc-sub" x="561.5" y="77" textAnchor="middle">Asha → Ravi</text></g></g><circle className="ngc-packet c-blue" r="5"><animateMotion dur="3.6s" repeatCount="indefinite" path="M78 106 L562 106" /></circle><text className="ngc-note" x="320" y="128" textAnchor="middle">ngOnChanges runs every time an @Input() gets a new value.</text>
</svg>
<figcaption>ngOnChanges receives the old and the new value of each changed input.</figcaption>
</figure>

It receives a `SimpleChanges` object. Each key is an input name, and each value has three properties:

- `currentValue`: the new value.
- `previousValue`: the old value (`undefined` on the first call).
- `firstChange`: `true` on the first call.

```angular runnable title="ngOnChanges reacts to a new input value"
import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';

@Component({
  selector: 'app-price-tag',
  template: `<p>Price: ₹{{ price }} {{ trend }}</p>`,
})
export class PriceTag implements OnChanges {
  @Input() price = 0;
  trend = '';

  ngOnChanges(changes: SimpleChanges) {
    const change = changes['price'];
    if (change.firstChange) {
      this.trend = '(new)';
    } else {
      this.trend = change.currentValue > change.previousValue ? '▲ up' : '▼ down';
    }
  }
}

@Component({
  selector: 'app-root',
  imports: [PriceTag],
  template: `
    <app-price-tag [price]="price" />
    <button (click)="price = price + 50">Raise price</button>
    <button (click)="price = price - 50">Lower price</button>
  `,
})
export class App {
  price = 1000;
}
```

`ngOnChanges` runs only when the input receives a **new reference**. Changing a property inside an object input, such as `user.name = 'Ravi'`, does not trigger it. Pass a new object instead: `user = { ...user, name: 'Ravi' }`.

## `ngOnInit`

`ngOnInit` runs **once**, after Angular sets the first input values. It is the standard place to start the component's work: load data, read inputs, or set up initial state.

Use `ngOnInit` instead of the constructor for this work. In the constructor, the inputs are not set yet.

```angular runnable title="ngOnInit loads data after the inputs are ready"
import { Component, Input, OnInit } from '@angular/core';

@Component({
  selector: 'app-order-summary',
  template: `
    <p>Constructor saw customerId: {{ seenInConstructor }}</p>
    <p>ngOnInit saw customerId: {{ customerId }}</p>
    <p>{{ status }}</p>
  `,
})
export class OrderSummary implements OnInit {
  @Input() customerId = 0;
  seenInConstructor = 0;
  status = '';

  constructor() {
    this.seenInConstructor = this.customerId;
  }

  ngOnInit() {
    this.status = 'Loading orders for customer ' + this.customerId + '…';
  }
}

@Component({
  selector: 'app-root',
  imports: [OrderSummary],
  template: `<app-order-summary [customerId]="42" />`,
})
export class App {}
```

The constructor sees the default value `0`. `ngOnInit` sees `42`, the value the parent passed.

## `ngDoCheck`

`ngDoCheck` runs during **every** change detection run, after `ngOnChanges` and `ngOnInit`. Angular runs change detection after events, timers and HTTP responses, so this hook can run very often.

Use it only to detect changes that Angular does not track by itself, such as a property changed inside an object input. Keep the code very fast.

```angular runnable title="ngDoCheck detects a change inside an object"
import { Component, DoCheck, Input } from '@angular/core';

interface Cart {
  items: string[];
}

@Component({
  selector: 'app-cart-badge',
  template: `<p>🛒 {{ itemCount }} item(s)</p>`,
})
export class CartBadge implements DoCheck {
  @Input() cart: Cart = { items: [] };
  itemCount = 0;

  ngDoCheck() {
    if (this.cart.items.length !== this.itemCount) {
      this.itemCount = this.cart.items.length;
    }
  }
}

@Component({
  selector: 'app-root',
  imports: [CartBadge],
  template: `
    <app-cart-badge [cart]="cart" />
    <button (click)="cart.items.push('Item ' + (cart.items.length + 1))">Add item</button>
  `,
})
export class App {
  cart = { items: ['Item 1'] };
}
```

The button changes the array inside the same `cart` object, so `ngOnChanges` does not run. `ngDoCheck` notices the new length.

`ngDoCheck`, `ngAfterContentChecked` and `ngAfterViewChecked` run on every change detection cycle. Slow code in these hooks slows down the whole app. Prefer passing a new object to the input, so `ngOnChanges` can do the work instead.

## `ngAfterContentInit`

**Content projection** lets a parent place its own HTML inside a child component's tag. The child marks where that HTML goes with `<ng-content />`.

<figure className="ngc-fig">
<svg viewBox="0 0 640 234" role="img" aria-label="Content is HTML that the parent puts between the tags; the view is the component own template.">
<defs><marker id="ngc-l2" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0 0L10 5L0 10z" className="ngc-arrowhead" /></marker></defs><text className="ngc-title c-orange" x="160" y="22" textAnchor="middle">Content (from the parent)</text><text className="ngc-title c-blue" x="480" y="22" textAnchor="middle">View (the own template)</text><g className="ngc-step ngc-t1"><g className="ngc-node ngc-soft c-orange"><rect x="30" y="36" width="260" height="46" rx="9" /><text x="160.0" y="56" textAnchor="middle" style={{fontSize:"13px"}}>&lt;app-card&gt; ... &lt;/app-card&gt;</text><text className="ngc-sub" x="160.0" y="74" textAnchor="middle">HTML between the tags</text></g></g><line className="ngc-link ngc-flow c-slate" x1="160" y1="83" x2="160" y2="99" markerEnd="url(#ngc-l2)" /><g className="ngc-step ngc-t2"><g className="ngc-node ngc-soft c-orange"><rect x="30" y="100" width="260" height="46" rx="9" /><text x="160.0" y="120" textAnchor="middle" style={{fontSize:"13px"}}>&lt;ng-content /&gt;</text><text className="ngc-sub" x="160.0" y="138" textAnchor="middle">where it is shown</text></g></g><line className="ngc-link ngc-flow c-slate" x1="160" y1="147" x2="160" y2="163" markerEnd="url(#ngc-l2)" /><g className="ngc-step ngc-t3"><g className="ngc-node ngc-soft c-orange"><rect x="30" y="164" width="260" height="46" rx="9" /><text x="160.0" y="184" textAnchor="middle" style={{fontSize:"13px"}}>ngAfterContentInit</text><text className="ngc-sub" x="160.0" y="202" textAnchor="middle">content is ready</text></g></g><g className="ngc-step ngc-t1"><g className="ngc-node ngc-soft c-blue"><rect x="350" y="36" width="260" height="46" rx="9" /><text x="480.0" y="56" textAnchor="middle" style={{fontSize:"13px"}}>template: ...</text><text className="ngc-sub" x="480.0" y="74" textAnchor="middle">written in the component</text></g></g><line className="ngc-link ngc-flow c-slate" x1="480" y1="83" x2="480" y2="99" markerEnd="url(#ngc-l2)" /><g className="ngc-step ngc-t2"><g className="ngc-node ngc-soft c-blue"><rect x="350" y="100" width="260" height="46" rx="9" /><text x="480.0" y="120" textAnchor="middle" style={{fontSize:"13px"}}>child components</text><text className="ngc-sub" x="480.0" y="138" textAnchor="middle">created from the template</text></g></g><line className="ngc-link ngc-flow c-slate" x1="480" y1="147" x2="480" y2="163" markerEnd="url(#ngc-l2)" /><g className="ngc-step ngc-t3"><g className="ngc-node ngc-soft c-blue"><rect x="350" y="164" width="260" height="46" rx="9" /><text x="480.0" y="184" textAnchor="middle" style={{fontSize:"13px"}}>ngAfterViewInit</text><text className="ngc-sub" x="480.0" y="202" textAnchor="middle">view is ready</text></g></g>
</svg>
<figcaption>Content comes from the parent. The view is the component's own template.</figcaption>
</figure>

`ngAfterContentInit` runs **once**, after Angular has placed the projected content inside the child. Use it when the component must read or count that content.

```angular runnable title="ngAfterContentInit reads the projected content"
import { AfterContentInit, Component, ContentChildren, Directive, QueryList } from '@angular/core';

@Directive({ selector: '[appTab]' })
export class TabMarker {}

@Component({
  selector: 'app-tab-bar',
  template: `<div class="bar"><ng-content /></div><p>{{ info }}</p>`,
  styles: `.bar { display: flex; gap: 8px; }`,
})
export class TabBar implements AfterContentInit {
  @ContentChildren(TabMarker) tabs!: QueryList<TabMarker>;
  info = '';

  ngAfterContentInit() {
    this.info = 'This tab bar received ' + this.tabs.length + ' tabs.';
  }
}

@Component({
  selector: 'app-root',
  imports: [TabBar, TabMarker],
  template: `
    <app-tab-bar>
      <button appTab>Overview</button>
      <button appTab>Orders</button>
      <button appTab>Settings</button>
    </app-tab-bar>
  `,
})
export class App {}
```

`@ContentChildren` collects the projected elements that carry the `appTab` directive. They are available from `ngAfterContentInit` onward.

## `ngAfterContentChecked`

`ngAfterContentChecked` runs after `ngAfterContentInit`, and again after **every** change detection check of the projected content.

Use it only when the component must react each time the projected content may have changed, for example to recount items. Like `ngDoCheck`, it runs often, so keep it fast.

```ts
ngAfterContentChecked() {
  // Runs after every check of the projected content.
  this.tabCount = this.tabs.length;
}
```

## `ngAfterViewInit`

`ngAfterViewInit` runs **once**, after Angular has created the component's own view and the views of its children.

It is the first moment when elements from the template are available through `@ViewChild`. Use it to focus an input, measure an element, or start a third-party library that needs a DOM element, such as a chart.

```angular runnable title="ngAfterViewInit focuses an input and measures an element"
import { AfterViewInit, Component, ElementRef, ViewChild, signal } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <input #search placeholder="The cursor starts here" />
    <div #banner class="banner">Spring sale: 20% off</div>
    <p>Banner width: {{ width() }}px</p>
  `,
  styles: `.banner { display: inline-block; padding: 8px 16px; background: #fde68a; }`,
})
export class App implements AfterViewInit {
  @ViewChild('search') search!: ElementRef<HTMLInputElement>;
  @ViewChild('banner') banner!: ElementRef<HTMLDivElement>;
  width = signal(0);

  ngAfterViewInit() {
    this.search.nativeElement.focus();
    this.width.set(this.banner.nativeElement.offsetWidth);
  }
}
```

`width` is a **signal**. Setting a signal tells Angular to check the view again. If `width` were a plain property, changing it here would make Angular report `ExpressionChangedAfterItHasBeenCheckedError` in development mode, because the view has already been checked.

| Point | `ngAfterContentInit` | `ngAfterViewInit` |
|---|---|---|
| Waits for | Projected content (`<ng-content>`) | The component's own template and child views |
| Query decorator | `@ContentChildren`, `@ContentChild` | `@ViewChildren`, `@ViewChild` |
| Typical use | Count or configure projected items | Focus, measure, start DOM libraries |

## `ngAfterViewChecked`

`ngAfterViewChecked` runs after `ngAfterViewInit`, and again after **every** check of the component's view.

Use it rarely, for example to keep a chat window scrolled to the newest message after the list re-renders. Never change displayed values here without care, because that starts another check.

```ts
ngAfterViewChecked() {
  // Keep the message list scrolled to the bottom.
  const list = this.messages.nativeElement;
  list.scrollTop = list.scrollHeight;
}
```

## `ngOnDestroy`

`ngOnDestroy` runs **once**, just before Angular removes the component, for example when an `@if` becomes false or the user navigates away.

<figure className="ngc-fig">
<svg viewBox="0 0 640 134" role="img" aria-label="The component is removed; Angular calls ngOnDestroy, where timers and subscriptions are stopped.">
<defs><marker id="ngc-l3" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0 0L10 5L0 10z" className="ngc-arrowhead" /></marker></defs><g className="ngc-step ngc-t1"><g className="ngc-node ngc-solid c-blue"><rect x="6" y="30" width="145" height="64" rx="9" /><text x="78.5" y="59" textAnchor="middle" style={{fontSize:"14px"}}>@if becomes false</text><text className="ngc-sub" x="78.5" y="77" textAnchor="middle">or the route changes</text></g></g><line className="ngc-link ngc-flow c-slate" x1="152" y1="62" x2="166" y2="62" markerEnd="url(#ngc-l3)" /><g className="ngc-step ngc-t2"><g className="ngc-node ngc-soft c-red"><rect x="167" y="30" width="145" height="64" rx="9" /><text x="239.5" y="59" textAnchor="middle" style={{fontSize:"14px"}}>ngOnDestroy()</text><text className="ngc-sub" x="239.5" y="77" textAnchor="middle">called once</text></g></g><line className="ngc-link ngc-flow c-slate" x1="313" y1="62" x2="327" y2="62" markerEnd="url(#ngc-l3)" /><g className="ngc-step ngc-t3"><g className="ngc-node ngc-soft c-orange"><rect x="328" y="30" width="145" height="64" rx="9" /><text x="400.5" y="59" textAnchor="middle" style={{fontSize:"14px"}}>Clean up</text><text className="ngc-sub" x="400.5" y="77" textAnchor="middle">clearInterval, unsubscribe</text></g></g><line className="ngc-link ngc-flow c-slate" x1="474" y1="62" x2="488" y2="62" markerEnd="url(#ngc-l3)" /><g className="ngc-step ngc-t4"><g className="ngc-node ngc-soft c-green"><rect x="489" y="30" width="145" height="64" rx="9" /><text x="561.5" y="59" textAnchor="middle" style={{fontSize:"14px"}}>Removed</text><text className="ngc-sub" x="561.5" y="77" textAnchor="middle">no code keeps running</text></g></g><circle className="ngc-packet c-red" r="5"><animateMotion dur="3.6s" repeatCount="indefinite" path="M78 106 L562 106" /></circle><text className="ngc-note" x="320" y="128" textAnchor="middle">Without clean-up, timers keep running after the component is gone.</text>
</svg>
<figcaption>ngOnDestroy is the last chance to stop work that the component started.</figcaption>
</figure>

Use it to clean up anything that would keep running after the component is gone:

- Timers started with `setInterval` or `setTimeout`.
- Subscriptions to observables, such as live data streams.
- WebSocket connections and global event listeners on `window` or `document`.

```angular runnable title="ngOnDestroy stops the timer when the component is removed"
import { Component, OnDestroy, OnInit, signal } from '@angular/core';

@Component({
  selector: 'app-session-timer',
  template: `<p>Session time: {{ seconds() }}s</p>`,
})
export class SessionTimer implements OnInit, OnDestroy {
  seconds = signal(0);
  private timerId?: ReturnType<typeof setInterval>;

  ngOnInit() {
    this.timerId = setInterval(() => this.seconds.update((s) => s + 1), 1000);
  }

  ngOnDestroy() {
    clearInterval(this.timerId);
    console.log('Timer stopped after ' + this.seconds() + 's');
  }
}

@Component({
  selector: 'app-root',
  imports: [SessionTimer],
  template: `
    <button (click)="active = !active">{{ active ? 'Sign out' : 'Sign in' }}</button>
    @if (active) { <app-session-timer /> }
  `,
})
export class App {
  active = true;
}
```

Wait a few seconds, then click **Sign out**. The console shows the message from `ngOnDestroy`. Without `clearInterval`, the timer would keep running in the background.

## Lifecycle Best Practices

- **Keep the constructor light.** Use it only for dependency injection. Start work in `ngOnInit`.
- **Clean up in `ngOnDestroy`.** Every timer, subscription and global listener you start must be stopped.
- **Use `ngOnChanges` to react to inputs.** Compare `previousValue` and `currentValue` instead of polling in `ngDoCheck`.
- **Pass new objects to inputs.** Replace an object instead of changing it in place, so `ngOnChanges` runs.
- **Access template elements in `ngAfterViewInit`**, not earlier. `@ViewChild` is not set before it.
- **Avoid the "Checked" hooks** unless you have a measured need. They run on every change detection cycle.
- **Implement the interfaces**, such as `implements OnInit`, so a misspelled hook name is reported by the compiler.

## Common Mistakes

<div className="ngc-mistakes">

| Mistake | What you see | Fix |
|---|---|---|
| Reading an input in the constructor | The default value, not the parent's value | Read inputs in `ngOnInit` or `ngOnChanges` |
| Misspelled hook, such as `ngOnint` | The method never runs, with no error | Add `implements OnInit` so the compiler checks the name |
| Using `@ViewChild` in `ngOnInit` | `Cannot read properties of undefined` | Use it in `ngAfterViewInit` |
| Not clearing a timer | The timer keeps running after the component is gone | Call `clearInterval` in `ngOnDestroy` |
| Changing a plain displayed property in `ngAfterViewInit` | `ExpressionChangedAfterItHasBeenCheckedError` | Store the value in a signal, or set it earlier |
| Mutating an object input | `ngOnChanges` does not run | Pass a new object: `cart = { ...cart, items: [...] }` |

</div>

## Check Yourself

## Exercises

## What to Remember

- Angular calls lifecycle hooks in a fixed order: `ngOnChanges`, `ngOnInit`, `ngDoCheck`, the content hooks, the view hooks, and finally `ngOnDestroy`.
- Start work in `ngOnInit`, react to inputs in `ngOnChanges`, and use template elements from `ngAfterViewInit` onward.
- Clean up timers, subscriptions and listeners in `ngOnDestroy`.

Next: **Template-Driven Forms**, how to collect user input with forms built in the template.
