OnPush change detection strategy in Angular

Wojciech Trawiński
YouGov Front-end meeting, 2021
Angular performance tips

Angular performance tips
- OnPush change detection strategy,
- modules and components lazy-loading,
- taking control over change detection process,
- pure pipes,
- memoization,
- setters vs ngOnChanges
Change detection

Change detection
NgZone
Change detection

Default strategy
NgZone
Change detection


Default strategy
OnPush strategy
NgZone
Change detection


Default strategy
OnPush strategy
NgZone
Asynchronous events
OnPush strategy triggerers

OnPush strategy triggerers
- Input property change (compared by reference),
- event dispatched in the DOM sub-tree (with listener),
- markForCheck method's call (ChangeDetectorRef).
Immutability

Immutability
mutable update pattern
interface User {
name: string;
age: number;
}
...
public user: User = { name: "Max", age: 30 };
...
public changeUserAge(age: number): void {
this.user.age = age;
}Immutability
mutable update pattern
immutable update pattern
interface User {
name: string;
age: number;
}
...
public user: User = { name: "Max", age: 30 };
...
public changeUserAge(age: number): void {
this.user.age = age;
}interface User {
name: string;
age: number;
}
...
public user: User = { name: "Max", age: 30 };
...
public changeUserAge(age: number): void {
this.user = { ...this.user, age };
}Asynchronous events

Asynchronous events
timers
public date: string = null;
private dateInterval = null;
...
public showDate(): void {
this.dateInterval = setInterval(() => {
this.date = new Date().toISOString();
}, 1000);
}
Asynchronous events
timers
XHR
public date: string = null;
private dateInterval = null;
...
public showDate(): void {
this.dateInterval = setInterval(() => {
this.date = new Date().toISOString();
}, 1000);
}
public remoteData: string = null;
...
public loadData(): void {
mockResponse$.subscribe(res => {
this.remoteData = res;
});
}AsyncPipe

AsyncPipe
- automatically (un)subscribes to(from) stream,
- handles stream's reference change,
- calls the markForCheck method,
- does not apply distinctUntilChanged operator.
AsyncPipe
- automatically (un)subscribes to(from) stream,
- handles stream's reference change,
- calls the markForCheck method,
- does not apply distinctUntilChanged operator.
AsyncPipe
export function select<T, Props, K>(
pathOrMapFn: ((state: T, props?: Props) => any) | string,
propsOrPath?: Props | string,
...paths: string[]
) {
return function selectOperator(source$: Observable<T>): Observable<K> {
let mapped$: Observable<any>;
...
return mapped$.pipe(distinctUntilChanged());
};
}with NgRx selectors
ngDoCheck method

ngDoCheck method
- top-level component with OnPush strategy,
- called frequently,
- manual checks and the markForCheck method call.
ngDoCheck method
- top-level component with OnPush strategy,
- called frequently,
- manual checks and the markForCheck method call.
Other gotchas

Other gotchas
- events outside of the NgZone,
- custom form controls,
- binding updates is a part of the change detection process.
OnPush by default

OnPush by default
{
...
"schematics": {
"@schematics/angular:component": {
"changeDetection": "OnPush"
}
},
...
}angular.json
Conclusions

Conclusions
- favour immutable update patterns,
- subscribe using the AsyncPipe,
- prevent unnecessary updates (distinctUntilChanged),
- call markForCheck/detectChanges method as the last resort,
- bindings get updated as a part of the change detection.
OnPush change detection strategy in Angular
By wojtrawi
OnPush change detection strategy in Angular
- 992