DEV Community

HarmonyOS
HarmonyOS

Posted on • Edited on

🚀 ForEach & LazyForEach in HarmonyOS Next: When to Use Which?

Read the original article:🚀 ForEach & LazyForEach in HarmonyOS Next: When to Use Which?

Introduction

If you’re developing apps with HarmonyOS Next, displaying lists of data in the UI is inevitable. Especially when working with large datasets, maintaining smooth performance is critical.

In this article, we’ll compare two powerful structures — ForEach and LazyForEach — highlighting their differences with examples and performance insights.

🧪 1. ForEach — Load Everything Upfront

👇 What Happens?

All data items are rendered into the UI at once.
If you have 100 products, all 100 are loaded into memory immediately.

📦 Code Example:

@State productList: Array<Products> = [];

  async aboutToAppear() {
    this.init()
  }

  onPageShow(): void {
    this.init()
  }

  async init() {
    this.productList = await fetchAllProducts(getContext(this));
  }

  build() {

    Grid() {
      ForEach(this.productList, (product: Products) => {
        GridItem() {
          ProductCard({ product: product })
            .onClick(() => {
              router.pushNamedRoute({
                name: 'ProductDetail',
                params: product
              })
            })
        }
      });
    }
}
Enter fullscreen mode Exit fullscreen mode
ForEach-ProductList.ets

📸 Visual Suggestion:

🖼️ A scrolling list fully loaded from top to bottom with a transparent overlay animation indicating all items are preloaded.
🎨 Suggestion: LottieFiles — “List Loading” animation.

🧯 Note:

Works well for small datasets, but can cause slowdowns with large lists.

🦥 2. LazyForEach — Load Only What’s Needed

👇 What Happens?

Only the data items visible on the screen are loaded. As you scroll, new items are loaded dynamically in the background.

📦 Code Example:

@State productList: Array<Products> = [];
  @State productDataSource: LazyDataSource<Products> = new LazyDataSource();

  async aboutToAppear() {
    this.init()
  }

  onPageShow(): void {
    this.init()
  }

  async init() {
    this.productList = await fetchAllProducts(getContext(this));
    this.productList.forEach((pr: Products) => {
      this.productDataSource.pushData(pr);
    })
  }

  build() {

    Grid() {
      LazyForEach(this.productDataSource, (product: Products) => {
        GridItem() {
          ProductCard({ product: product })
            .onClick(() => {
              router.pushNamedRoute({
                name: 'ProductDetail',
                params: product
              })
            })
        }
      });
    }

}
Enter fullscreen mode Exit fullscreen mode
LazyForEach Product List

📸 Visual Suggestion:

🖼️ Only the visible items are fully loaded, while items below are shown as “cloudy” or blurred with a subtle loading animation.
🎨 Suggestion: LottieFiles — “Scroll Lazy Load” animation.

🧠 Advantages:

  • Much faster initial load time

  • Reduced memory usage

  • Smoother scrolling performance

⚙️ LazyDataSource & BasicDataSource Structures

When using LazyForEach, you need to load your data through the LazyDataSource and BasicDataSource classes.

LazyDataSource.ets

@Observed
export default class LazyDataSource<T> extends BasicDataSource<T> {
  dataArray: T[] = [];

  public totalCount(): number {
    return this.dataArray.length;
  }

  public getData(index: number): T {
    return this.dataArray[index];
  }

  public addData(index: number, data: T): void {
    this.dataArray.splice(index, 0, data);
    this.notifyDataAdd(index);
  }

  public pushData(data: T): void {
    this.dataArray.push(data);
    this.notifyDataAdd(this.dataArray.length - 1);
  }

  public pushArrayData(newData: ObservedArray<T>): void {
    this.clear();
    this.dataArray.push(...newData);
    this.notifyDataReload();
  }

  public appendArrayData(addData: ObservedArray<T>): void {
    this.dataArray.push(...addData);
    this.notifyDataReload();
  }

  public deleteData(index: number): void {
    this.dataArray.splice(index, 1);
    this.notifyDataDelete(index);
  }

  public getDataList(): ObservedArray<T> {
    return this.dataArray;
  }

  public clear(): void {
    this.dataArray.splice(0, this.dataArray?.length);
  }

  public isEmpty(): boolean {
    return this.dataArray.length === 0;
  }
}

Enter fullscreen mode Exit fullscreen mode
LazyDataSource.ets

BasicDataSource.ets

const TAG = '[BasicDataSource]';

class BasicDataSource<T> implements IDataSource {
  private listeners: DataChangeListener[] = [];

  public totalCount(): number {
    return 0;
  }

  public getData(index: number): T | undefined {
    return undefined;
  }

  registerDataChangeListener(listener: DataChangeListener): void {
    if (this.listeners.indexOf(listener) < 0) {
      Logger.info(TAG, 'add listener');
      this.listeners.push(listener);
    }
  }

  unregisterDataChangeListener(listener: DataChangeListener): void {
    const pos = this.listeners.indexOf(listener);
    if (pos >= 0) {
      Logger.info(TAG, 'remove listener');
      this.listeners.splice(pos, 1);
    }
  }

  notifyDataReload(): void {
    this.listeners.forEach(listener => {
      listener.onDataReloaded();
    })
  }

  notifyDataAdd(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataAdd(index);
    })
  }

  notifyDataChange(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataChange(index);
    })
  }

  notifyDataDelete(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataDelete(index);
    })
  }

  notifyDataMove(from: number, to: number): void {
    this.listeners.forEach(listener => {
      listener.onDataMove(from, to);
    })
  }
}

Enter fullscreen mode Exit fullscreen mode
BasicDataSource.ets

📌 Conclusion & Recommendations

🔹 If your list has fewer than 20 items, ForEach works just fine — no need to complicate things.
🔹 But if you’re dealing with 100+ items, LazyForEach is the way to go for performance and smooth UX.

🎯 Tip for beginners: LazyForEach may seem complex initially, but it’s essential for modern apps. Once you see the performance boost, you won’t want to go back!

📚 Resources

(https://forums.developer.huawei.com/forumPortal/en/topic/0203189331981188012?fid=0102647487706140266)

Top comments (0)