Angular Change Detection Strategies: Default vs. OnPush

Angular Change Detection Strategies: Default vs. OnPush

Change detection is a critical aspect of Angular’s data binding mechanism. It determines when and how Angular updates the user interface based on changes in the application’s data. Angular offers different change detection strategies to optimize performance and control the update process.

In this article, we’ll explore two popular change detection strategies: Default and OnPush. We’ll discuss their characteristics, advantages, and use cases, and provide a real-world example to demonstrate their behavior. Let’s dive in!

Default Change Detection Strategy

The Default change detection strategy is the default behavior used by Angular. It checks all components for changes during each cycle of change detection. When a component’s input properties or its associated template bindings change, Angular triggers a re-render of that component and all of its child components.

Example: Parent and Child Components with Default Strategy

Let’s consider a simple scenario where we have a parent component and two child components: ChildDefaultComponent and ChildOnPushComponent. The parent component has a counter, and clicking a button increments the counter.

// Parent Component
@Component({
  selector: 'app-parent',
  template: `
    <h2>Parent Component</h2>
    <button (click)="increment()">Increment Counter</button>
    <app-child-default [counter]="counter"></app-child-default>
    <app-child-onpush [counter]="counter"></app-child-onpush>
  `,
})
export class ParentComponent {
  counter = 0;

  increment() {
    this.counter++;
  }
}

// Child Component with Default Strategy
@Component({
  selector: 'app-child-default',
  template: `
    <h4>Child Component (Default)</h4>
    <p>Counter: {{ counter }}</p>
    <p>Timer: {{ timer }}</p>
  `,
})
export class ChildDefaultComponent implements OnInit, OnDestroy {
  @Input() counter!: number;
  timer: number = 0;
  private intervalId: any;

  ngOnInit() {
    this.intervalId = setInterval(() => {
      this.timer++;
    }, 1000);
  }

  ngOnDestroy() {
    clearInterval(this.intervalId);
  }
}

In this example, both child components receive the counter input property from the parent component. Additionally, both child components have a timer property that increments every second using a setInterval function in the ngOnInit lifecycle hook.

The Default change detection strategy ensures that both the counter and timer properties in ChildDefaultComponent trigger re-renders whenever they change. So, when we click the “Increment Counter” button in the parent component, both child components will be re-rendered, updating the displayed counter and timer values.

In the Default strategy, changes in the parent component trigger the change detection process for all its child components and their descendants. This includes re-rendering all components, regardless of whether the changes directly affect them or not.

OnPush Change Detection Strategy

The OnPush change detection strategy is designed to optimize performance by reducing the number of components that require re-rendering. With this strategy, Angular only checks components for changes when their input properties change or when an event is fired from within the component itself.

Example: Parent and Child Components with OnPush Strategy

Now let’s modify our previous example to use the OnPush change detection strategy in the ChildOnPushComponent. This change will demonstrate how the OnPush strategy avoids unnecessary re-renders when input properties don’t change.


// Child Component with OnPush Strategy
@Component({
  selector: 'app-child-onpush',
  template: `
    <h4>Child Component (OnPush)</h4>
    <p>Counter: {{ counter }}</p>
    <p>Timer: {{ timer }}</p>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ChildOnPushComponent implements OnInit, OnDestroy {
  @Input() counter!: number;
  timer: number = 0;
  private intervalId: any;

  ngOnInit() {
    this.intervalId = setInterval(() => {
      this.timer++;
    }, 1000);
  }

  ngOnDestroy() {
    clearInterval(this.intervalId);
  }
}

With the OnPush strategy applied to ChildOnPushComponent, the timer property will not trigger a re-render when the counter changes. The timer property is not directly affected by changes in input properties, so it doesn’t require re-evaluation.

The sequence diagram illustrates the interaction between different components in an Angular application, specifically focusing on the change detection strategy. Let’s break it down step by step:

ParentChildDefaultChildOnPushGrandchildDefaultGrandchildOnPushInput/Event Changes (Default)Input/Event Changes (OnPush)Input/Event Changes (Default)Input/Event Changes (OnPush)Re-render (Default)Update Counter (Default)Update Timer (Default)Re-render (Default)opt[Default Change Detection Strategy]No Re-render (OnPush)Update Counter (OnPush)Update Counter (OnPush)Update Timer (OnPush)opt[OnPush Change Detection Strategy]ParentChildDefaultChildOnPushGrandchildDefaultGrandchildOnPush

In this diagram, the Parent component triggers input or event changes to both ChildDefault and ChildOnPush components. With the default change detection strategy, all changes in the counter and timer properties will trigger re-renders in both ChildDefault and GrandchildDefault components.

With the OnPush change detection strategy, the counter property changes in ChildOnPush component will trigger re-rendering, but the timer property will not. Additionally, the counter change in ChildOnPush component will be passed to GrandchildOnPush component, triggering a re-render there as well.

Conclusion

Understanding Angular’s change detection strategies is essential for optimizing performance in your applications. By leveraging the Default strategy, you ensure that all components are checked for changes and re-rendered as needed. On the other hand, the OnPush strategy allows you to limit the re-rendering process to components that are directly affected by changes, resulting in improved performance.

By applying the appropriate change detection strategy based on your application’s requirements, you can achieve better performance and more efficient updates to the user interface.

Next Post Previous Post
No Comment
Add Comment
comment url