Angular: onPush Change Detection Strategy
Before diving deep into the angular onPush change detection strategy it is important to know what is change detection and how the default change detection strategy works in angular. You can learn more about it from my previous blog.
The “OnPush” change detection strategy in Angular is useful where performance optimization is a top priority. It is particularly effective when dealing with complex applications with large component trees, as it reduces the number of checks performed by Angular during change detection, leading to improved performance.
To start with :
How does onPush change detection strategy work?
When a component uses onPush change detection strategy, it checks for change detection only if the component is marked as dirty.
There are many ways by which a component can be marked as dirty. The @Input binding is one of the most common and important ways to mark a component as dirty. By understanding and implementing this, you can greatly improve your system’s performance.
Let's dive deep into the effect of change detection on @input binding.
@input binding is used for passing data from the parent to the child component. In @input binding, if there is a change in the @input data, the component is marked as dirty and hence angular runs change detection for that component.
Also, angular performs a shallow comparison of the @input data. i.e it checks the reference of the @input data, if the reference of the data changes then only it marks the component as dirty.
In javascript, the datatypes such as string, and numbers are immutable. i.e any operation that appears to modify a string actually creates a new reference.
Whereas, datatypes like arrays and objects are mutable. i.e any operation that modifies an array/object does not create a new reference.
Let's understand it with the help of an example: Below I have attached dummy code snippets, it's just for understanding the concept.
1. Example of @input binding with immutable datatype:
In the above example, In the app-root component, we have an input element ‘title’ that is passed on to the app-child-component. The app-child-component uses onPush change detection strategy. The ‘title’ in the app-root (parent component) is a string, so when it is updated, its reference is updated as well. The app-child-component is marked as dirty, and the angular change detection cycle is executed to reflect the updated value of ‘title’ in the app-child-component.
2. Example of @input binding with mutable datatype:
Considering the same example, The difference here is that we are using an object instead of a string.
Here, the input element passed is ‘data’ which is an object. So when the value of the ‘data.title’ is updated there is no change in the reference and hence the app-component-child will not be marked as dirty, and the angular change detection cycle will not be executed in the app-component-child. As a result, the updated value of ‘data.title’ is not reflected.
But if using onPush() strategy does not work with mutable objects then what's the use?
- There are ways in which onPush() change detection strategy can be used for mutable objects. I will discuss each of the strategies in detail in the next few examples. Using these simple solutions will help you to largely improve the performance of your angular application.
- By updating the reference of the mutable object
In the above example the value of ‘data.title’ was not getting updated as there was no change in the reference. So, to mark the component as dirty we need to change the reference of the object. How can we do it?
In this example, a function named onUpdate() is called whenever the input element of the ‘app-root’ component changes. The onUpdate() function accepts the $event as a parameter which contains the value of the input field. The onUpdate() function, updates the data object in a way that the reference of the object is changed. Due to this, the child component i.e ‘app-child-component’ is marked as dirty and checks for change detection, displaying the latest value of ‘data.title’.
2. By manually marking the component as dirty
In the previous example, we changed the reference of the data object to mark the component as dirty. But what if we want to update ‘data.title’ without updating the reference? In that case, we can manually mark the component as dirty. For that, we can inject changeDetectorRef
and use its method markForCheck
to indicate for Angular that this component needs to be checked for change detection.
In the above code snippet, in the child component to check if the @input variable i.e title is updated or not we use ngDoCheck() lifecycle hook. ngDoCheck() is executed before Angular will run change detection for the component but during the check of the parent component. So, we place our logic, inside this block to compare values and manually mark the component as dirty.
The decision to run NgDoCheck()
hook even if a component is OnPush
often causes confusion. But there's no inconsistency if you know that it's run as part of the parent component check and actually the change detection is not executed on the child component. Keep in mind that ngDoCheck
is triggered only for top-most child component. In this way, the performance can be improved using onPush change detection strategy.
I hope, in this blog marking the component as dirty for @Input binding is clear.
There are many more ways, by which the component can be marked as dirty for onPush change detection strategy. You can refer it from these blogs: https://indepth.dev/posts/1515/deep-dive-into-the-onpush-change-detection-strategy-in-angular and https://blog.angular-university.io/onpush-change-detection-how-it-works/
Until then be happy and stay healthy!!!!!
Also, feel free to comment down below, if you have any doubts or suggestions…..