20

Use trackBy with Angular's ngFor

Tuesday, November 10, 2020

Whenever iterating over non trivial arrays consider using track by function to optimize for performance. What it does to improve performance? It will track the changes to the items based on the logic you write in that function. If used properly, only items that have changed will be repainted, thus improving the performance of long lists or complicated templates by huge amount.

To write such a track by function you need to decide how to determine that element has changed.

trackBy has to be a function with two parameters first is index (number) and second is the current item. Function should return value based on which angular change detection will determine if item has changed and so it should be repainted.

To identify good value to return in track by function, try to find what is uniquely identifies the item (item uid, article title, product name) or the change you intend to do to to the item (count, text value, ...), in some case combination of both is needed.

Most track by function are very straigt forward, lets imagine iterating over array of users, each one has unique id, to write such a function you'd do:

trackByFn(index, user) { return user.uid } .

You could improve this function to trackByFn(_, {uid}) { return uid } .

( _ instead of index, that way you don't get yelled at by typescript for not using the index param, and to improve readability you descructed the user param to just what you need {uid}) More explanation here Ignore fist parameter of function if unused with a simple _ (underscore)

To read more about trackBy visit official documentation or this nice blog post Improve Performance with trackBy

Instructions

Do

Use trackBy functions to identify changes in items you iterate over.

Avoid

Making changes (mutations) to you data in component.

Subscribing and asigning data to temporary variable in your component.

Consider

Use data stream directly in you template with async pipe and embrace OnPush change detection to improve performance.

Why

Automatic change detection in Angular can slow things down if the things get complicated. Think about how your data behave and adjust your code accordingly. Async pipe, OnPush and trackBy is the most bulletproof way against memory leaks and performance issues.

Code Examples

List of users tracking the change of uids from data stream

interface User { uid: string, name: string }

@Component({
  template: `
   <ul><li *ngFor="let item of users$ | async; trackBy:trackByFn">{{item.name}}</li></ul>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AppComponent {
  users$: Observable<User[]> = this.users.getUsers(); 

  trackBy:trackByFn(_, {uid}: User): string {
     return uid; 
  }

  constructor(users: UserService) {}
}

Iteration without explicit trackBy function

interface User { uid: string, name: string }

@Component({
  template: `
   <ul><li *ngFor="let item of users$ | async">{{item.name}}</li></ul>
  `
})
export class AppComponent {
  users$: Observable<User[]> = this.users.getUsers(); 

  constructor(users: UserService) {}
}
Vojtech Mašek

I am a software engineer focusing on front-end with expertise in Angular and TypeScript. I'm with Angular since its alpha phase. I'm also a Head of engineering and co-founder of FlowUp, a software development agency, where I lead multiple agile development teams.

Discussions are healthy ❤️