Make Your Angular App 100+ Times Faster with ‘OnPush’ Change Detection Strategy

This article is  the 2nd one in my “Angular’s Change Detection” series. If you want to start from the beginning, visit Angular Change Detection — Explained Shortly + NgZone basics.

How does Angular mark the View for check?

By default, Angular uses its Default Change Detection strategy ChangeDetectionStrategy.Default.

It waits for the triggering events (clicks, keyboard inputs, bindings changes, timers etc) and checks for changes all the components in the component tree from top to bottom.

How Angular component tree is checked for changes (source)

It is also known as dirty checking as far as each node in the tree goes through the checks even if they are not needed.

While dirty checking is really high-performant and handles thousands of checks per second, it can lead to performance issues when:

  • the component tree becomes too complex;
  • triggering events occur too often;

Luckily, there is a way to reduce both of the above problems by using another Change Detection Strategy — ChangeDetectionStrategy.OnPush

How does ChangeDetectionStrategy.OnPush work?

Enabling ChangeDetectionStrategy.OnPush

This mode forces the framework to check the component and its children only in the following cases:

  1. @Input’s reference is changed (comparing by “===” operator);
  2. The event is triggered by the component or one of its children (by @Outputs or @HostListeners);
  3. The “async” pipe gets a new value from the Observable;
  4. Change detection is triggered manually;
OnPush prevents Angular from unnecessary checks

From the diagram above you can see that by using OnPush in just a single component we significantly reduced amount of work that framework does on every single check.

What actions do NOT trigger change detection mechanism:

  1. Timers: setIntervalsetTimeout ;
  2. Values emitted by Promises and Observables;
  3. DOM events that are not listened to by Angular;
  4. @Inputs value change with the same reference;

Practice

Let’s take a look at the component created in the previous article about change detection and modify it to showcase how powerful OnPush strategy is.

Case 1

Component template
Component code

Code Overview

  1. When the component class is instanciated — we already have our fields greetingauthor and timestamp set with the initial values.
  2. After that in ngOnInit we have the 3 asynchronous actions that set new values for our component fields.
  3. Note that 4th one calls the detectChanges method of Angular’s change detector in 5 seconds.

So what is the result?

We can see that all the 3 async actions are triggered and each of them changes one of the component’s bindings. However, we do not see any changes rendered on the UI until the detectChanges() is called.

And then all the changes that we had made before are rendered simultaneously in one single check.

You can now analyze why all of the above happens. If you need a hint — scroll up to see what actions do NOT trigger change detection with OnPush.

Benefit: We can use OnPush to skip unnecessary checks. Especially for cases:

  1. When we want to skip rendering of particular values.
  2. When we load the data from different sources and want to show only the end result.

Case 2

Let’s look at the other example showing the crucial performance improvement when using a component with @Inputs.

Parent component:

Parent component code
Parent component template

Parent component contains the person’s data. Let’s assume we get this data from an HTTP call.

On the template we only display the child component and 2 buttons. Both of them change the person’s name. However, one of them updates fields values and the other one replaces object’s reference.

Child component:

Child component code

Child component is a typical “presentational” component that does nothing but accepts input from the parent. Note that it uses OnPush change detection strategy.

Let’s see the result:

Here we can see that in case when the Inputs properties change — Angular does not detect these changes.

This is happening because by enabling the OnPush strategy we tell the framework: “Hey, do not check this input until the object reference is changed”.

This gives us significant performance improvement because in the real-world apps we deal with the objects that with time get pretty heavy. And the only way for Angular to detect that something is changed within the object is to check each of its properties in each of the check cycles. You can only imagine the amount of work it does when its time to check dozens of components on the view and each of them usually has many imports inside.

Let’s say we received an Input with 30 properties and each of them has in average fields that are objects with just 10 properties inside. How much comparisons Angular needs to do on each check?

# of Comparisons = 30 x 3 x 10 = 900 !

And I bet it’s not even the worst scenario you’ve ever seen in your project 😉

And how many comparisons does Angular need to make to know that changes happened with OnPush option?

It’s only one check! oldValue === newValue. By enabling OnPush strategy we decreased the complexity of each check by 900 times.

And additional bonus — the quantity of checks is also decreased!

Benefit: By using OnPush strategy when the component accepts the non-primitive inputs from parent — we get significant performance increase because of multiplied effect of:

  1. Decreasing each single check complexity;
  2. Decreasing the total number of checks;

Summary

As you have seen — OnPush change detection strategy is a very powerful tool to dramatically increase Angular application performance.

In my work I use it as a default option in 95% of cases. Especially it perfectly fits the components that:

  • are “presentational” / “dumb” components;
  • contain complex asynchronous data flow;
  • accept massive Inputs;
  • have many simultaneous instances rendered on the view;
  • you want to have control of re-render flow;

👋🏻 Thank you for reading. This was the overview of OnPush Change Detection strategy in Angular.

👀 In the next post we will create an Ultimate Angular app Performance Checklist

✅ Follow me to not miss it out!

💬 Any your thoughts and ideas are very appreciated in the comments below


Additional Reading

https://mokkapps.de/blog/the-last-guide-for-angular-change-detection-you-will-ever-need/

Leave a Reply

Your email address will not be published. Required fields are marked *