Angular: Is trackBy necessary with ngFor?
Tags: angular, development, programming, frontend
Published: 2021-11-01 14:32:02

---
title: Angular: Is trackBy necessary with ngFor?
published: true
description: In a recent presentation, I had an individual very focused on the need for trackBy on my ngFor ... to the exclusion of everything else in the talk. He even went as far as to push a pull-request on my talk repository to add the change.
series: Angular
tags: Angular, Development, Programming, Frontend
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fx6faibroffcp4r54uba.png
---

In a recent presentation, I had an individual very focused on the need for `trackBy` on my `ngFor` ... to the exclusion of everything else in the talk. He even went as far as to push a pull-request on my talk repository to add the change.

I was perplexed.

1. I know there used to be a need for `trackBy`.
2. Experience has shown me little need for it's use recently.
3. Some quick research said that it is ***only*** needed if there are issues with performance with modern Angular.

So, I decided to test this out myself.

## The First Iteration

In my first iteration of testing, I tested loading data to see if there was a difference in load times when displaying them with and without the `trackBy`. Here are the first set of tests I ran that showed some hopeful results.

### Data

Basically, the data was 100,000 records with:

* An integer, 0-based `index`.
* An identifier that is a random 50 characters A-Z, a-z, or 0-9.

## Summary of First Iteration

From this point, I refreshed the page 25-times each, noting the time to finish on the Network tab.

Type    | WITH | WITHOUT
--------|------|--------
data 01 | 6.17 | 6.77
data 02 | 6.14 | 6.29
data 03 | 6.31 | 6.28
data 04 | 6.34 | 6.33
data 05 | 6.23 | 6.06
data 06 | 6.14 | 6.31
data 07 | 6.14 | 6.21
data 08 | 6.46 | 6.22
data 09 | 6.29 | 6.09
data 10 | 6.38 | 6.37
data 11 | 6.22 | 6.22
data 12 | 6.38 | 6.43
data 13 | 6.23 | 6.19
data 14 | 6.22 | 6.15
data 15 | 6.38 | 6.33
data 16 | 6.16 | 6.45
data 17 | 6.32 | 6.19
data 18 | 6.21 | 6.18
data 19 | 6.25 | 6.36
data 20 | 6.16 | 6.17
data 21 | 6.46 | 6.03
data 22 | 6.22 | 6.12
data 23 | 6.30 | 6.44
data 24 | 6.23 | 6.67
data 25 | 6.20 | 5.98
**RESULTS** | **6.26** | **6.27**

## Conclusion

The results were one-hundredth of a second different, my conclusion might be that `trackBy` would only be needed if there were some significant performance need.

This path only tested Angular's ability to place data on-screen. I realized that the `trackBy` was built as a means to allow for faster screen updates when the data changed.

So, on to the second iteration ...

## Second Iteration

I decided to go with a smaller data-set and initialize a series of changes where I swapped two indexes. I opted for a 500ms delay in between loading the data and initiating the swaps.

### The (Real) Work

The repository is [HERE](https://github.com/bob-fornal/angular-ngfor-trackby).

Basically, the data is now 10,000 records with:

* An integer, 0-based `index`.
* An identifier that is a random 50 characters A-Z, a-z, or 0-9.

Each page connects to the service, gets the data and displays the identifier 10,000 times. On one page, we are using `trackBy` and in the other, we are not using it.

Additionally, after 500ms another process triggers that randomly picks two indexes and swaps them. I did this 1,000 times per page.

### `data.service.ts`

The core to the service is as follows ...

```typescript
iterations: number = 1000;
dataPoints: number = 100000;
data: Array<any> = [];
startTime: Date;

constructor() {
  this.startTime = new Date();
  this.init();
}

init = (): void => {
  for (let i = 0, len = this.dataPoints; i < len; i++) {
    const datum: any = { index: i, identifier: this.makeid() };
    this.data.push(datum);
  }
};

makeid = (length: number = 50): string => {
  var result: string = '';
  var characters: string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  var charactersLength = characters.length;
   
  for ( var i = 0; i < length; i++ ) {
    result += characters.charAt(Math.floor(Math.random() * charactersLength));
  }
    
  return result;
};

getRandomInt = (min: number, max: number) => {
  min = Math.ceil(min);
  max = Math.floor(max);
  return Math.floor(Math.random() * (max - min + 1)) + min;
};
```

## `with.component.ts`

The components are nearly identical ...

```typescript
data: Array<any> = [];

constructor(
  private dataService: DataService
) { }

ngOnInit(): void {
  this.data = this.dataService.data;
  setTimeout(this.initiateReorganize.bind(this), 500);
}

ngAfterViewChecked(): void {
  const now: Date = new Date();
  const difference: number = now.getTime() - this.dataService.startTime.getTime();
  console.log('difference: ', difference);
}

identify = (index: number, item: any): string => item.index;

initiateReorganize = (): void => {
  const min: number = 0;
  const max: number = this.dataService.iterations - 1;
  for (let i = 0, len = this.dataService.iterations; i < len; i++) {
    const a: number = this.dataService.getRandomInt(min, max);
    const b: number = this.dataService.getRandomInt(min, max);
    [this.data[a], this.data[b]] = [this.data[b], this.data[a]];
  }
};
```

... except, the `without.component.ts` does not contain an `identify` function.

## `with.component.html`

And, the HTML is nearly identical, as well ...

```html
<div *ngFor="let item of data; trackBy: identify">
  {{ item.identifier }}
</div>
```

... removing the `trackBy: identify` in the `without.component.html`, as well.

## Results

From this point, I refreshed the page 25-times each, noting the time to finish in the console.

Type    | WITH | WITHOUT
--------|------|--------
Data 01 | 1700 | 1654
Data 02 | 1647 | 1669
Data 03 | 1634 | 1695
Data 04 | 1639 | 1652
Data 05 | 1753 | 1641
Data 06 | 1624 | 1693
Data 07 | 1627 | 1632
Data 08 | 1676 | 1637
Data 09 | 1638 | 1707
Data 10 | 1631 | 1630
Data 11 | 1625 | 1652
Data 12 | 1727 | 1648
Data 13 | 1633 | 1768
Data 14 | 1636 | 1641
Data 15 | 1684 | 1712
Data 16 | 1634 | 1667
Data 17 | 1690 | 1633
Data 18 | 1631 | 1730
Data 19 | 1624 | 1631
Data 20 | 1741 | 1640
Data 21 | 1635 | 1675
Data 22 | 1631 | 1641
Data 23 | 1690 | 1663
Data 24 | 1625 | 1652
Data 25 | 1688 | 1651
**Results** | **1658.52** | **1664.56**

## Conclusion

So, given that the results are 6.04 milliseconds different (remember, less than a second difference on load of a data-set 10-times larger), my conclusion would be that `trackBy` would only be needed if there were some significant performance need that cropped up.

In general, it is not needed in modern Angular.