1# LazyForEach: Lazy Data Loading
2
3For details about API parameters, see [LazyForEach](https://gitee.com/openharmony/docs/blob/master/en/application-dev/reference/apis-arkui/arkui-ts/ts-rendering-control-lazyforeach.md) APIs.
4
5**LazyForEach** iterates over provided data sources and creates corresponding components during each iteration. When **LazyForEach** is used in a scrolling container, the framework creates components as required within the visible area of the scrolling container. When a component is out of the visible area, the framework destroys and reclaims the component to reduce memory usage.
6
7## Constraints
8
9- **LazyForEach** must be used in a container component. Only the [List](../reference/apis-arkui/arkui-ts/ts-container-list.md), [Grid](../reference/apis-arkui/arkui-ts/ts-container-grid.md), [Swiper](../reference/apis-arkui/arkui-ts/ts-container-swiper.md), and [WaterFlow](../reference/apis-arkui/arkui-ts/ts-container-waterflow.md) components support lazy loading (that is, only the visible part and a small amount of data before and after the visible part are loaded for caching). For other components, all data is loaded at once.
10- In each iteration, only one child component must be created for **LazyForEach**. That is, the child component generation function of **LazyForEach** has only one root component.
11- The generated child components must be allowed in the parent container component of **LazyForEach**.
12- **LazyForEach** can be included in an **if/else** statement, and can also contain such a statement.
13- The ID generation function must generate a unique value for each piece of data. Rendering issues will arise with components assigned duplicate IDs.
14- **LazyForEach** must use the **DataChangeListener** object to re-render UI. If the first parameter **dataSource** is re-assigned a value, an exception occurs. When **dataSource** uses a state variable, the change of the state variable does not trigger the UI re-renders performed by **LazyForEach**.
15- For better rendering performance, when the **onDataChange** API of the **DataChangeListener** object is used to update the UI, an ID different from the original one needs to be generated to trigger component re-rendering.
16- **LazyForEach** must be used with the [@Reusable](https://developer.huawei.com/consumer/en/doc/best-practices-V5/bpta-component-reuse-V5#section5601835174020) decorator to trigger node reuse. Use @Reusable to decorate the components on the **LazyForEach** list. For details, see [Reuse Rules](https://developer.huawei.com/consumer/en/doc/best-practices-V5/bpta-component-reuse-V5#section5923195311402).
17
18## Key Generation Rules
19
20During **LazyForEach** rendering, the system generates a unique, persistent key for each item to identify the owing component. When the key changes, the ArkUI framework considers that the array element has been replaced or modified and creates a new component based on the new key.
21
22**LazyForEach** provides a parameter named **keyGenerator**, which is in effect a function through which you can customize key generation rules. If no **keyGenerator** function is defined, the ArkUI framework uses the default key generation function, that is, **(item: Object, index: number) => { return viewId + '-' + index.toString(); }**, wherein **viewId** is generated during compiler conversion. The **viewId** values in the same **LazyForEach** component are the same.
23
24## Component Creation Rules
25
26After the key generation rules are determined, the **itemGenerator** function – the second parameter in **LazyForEach** – creates a component for each array item of the data source based on the rules. There are two cases for creating a component: [initial render](#initial-render) and [non initial render](#non-initial-render).
27
28### Initial Render
29
30- ### Generating Different Key Values
31
32When used for initial render, **LazyForEach** generates a unique key for each array item of the data source based on the key generation rules, and creates a component.
33
34```ts
35// Basic implementation of IDataSource to handle data listener
36class BasicDataSource implements IDataSource {
37  private listeners: DataChangeListener[] = [];
38  private originDataArray: string[] = [];
39
40  public totalCount(): number {
41    return 0;
42  }
43
44  public getData(index: number): string {
45    return this.originDataArray[index];
46  }
47
48  // This method is called by the framework to add a listener to the LazyForEach data source.
49  registerDataChangeListener(listener: DataChangeListener): void {
50    if (this.listeners.indexOf(listener) < 0) {
51      console.info('add listener');
52      this.listeners.push(listener);
53    }
54  }
55
56  // This method is called by the framework to remove the listener from the LazyForEach data source.
57  unregisterDataChangeListener(listener: DataChangeListener): void {
58    const pos = this.listeners.indexOf(listener);
59    if (pos >= 0) {
60      console.info('remove listener');
61      this.listeners.splice(pos, 1);
62    }
63  }
64
65  // Notify LazyForEach that all child components need to be reloaded.
66  notifyDataReload(): void {
67    this.listeners.forEach(listener => {
68      listener.onDataReloaded();
69    })
70  }
71
72  // Notify LazyForEach that a child component needs to be added for the data item with the specified index.
73  notifyDataAdd(index: number): void {
74    this.listeners.forEach(listener => {
75      listener.onDataAdd(index);
76    })
77  }
78
79  // Notify LazyForEach that the data item with the specified index has changed and the child component needs to be rebuilt.
80  notifyDataChange(index: number): void {
81    this.listeners.forEach(listener => {
82      listener.onDataChange(index);
83    })
84  }
85
86  // Notify LazyForEach that the child component needs to be deleted from the data item with the specified index.
87  notifyDataDelete(index: number): void {
88    this.listeners.forEach(listener => {
89      listener.onDataDelete(index);
90    })
91  }
92
93  // Notify LazyForEach that data needs to be swapped between the from and to positions.
94  notifyDataMove(from: number, to: number): void {
95    this.listeners.forEach(listener => {
96      listener.onDataMove(from, to);
97    })
98  }
99}
100
101class MyDataSource extends BasicDataSource {
102  private dataArray: string[] = [];
103
104  public totalCount(): number {
105    return this.dataArray.length;
106  }
107
108  public getData(index: number): string {
109    return this.dataArray[index];
110  }
111
112  public addData(index: number, data: string): void {
113    this.dataArray.splice(index, 0, data);
114    this.notifyDataAdd(index);
115  }
116
117  public pushData(data: string): void {
118    this.dataArray.push(data);
119    this.notifyDataAdd(this.dataArray.length - 1);
120  }
121}
122
123@Entry
124@Component
125struct MyComponent {
126  private data: MyDataSource = new MyDataSource();
127   
128  aboutToAppear() {
129    for (let i = 0; i <= 20; i++) {
130      this.data.pushData(`Hello ${i}`)
131    }
132  }
133
134  build() {
135    List({ space: 3 }) {
136      LazyForEach(this.data, (item: string) => {
137        ListItem() {
138          Row() {
139            Text(item).fontSize(50)
140              .onAppear(() => {
141                console.info("appear:" + item)
142              })
143          }.margin({ left: 10, right: 10 })
144        }
145      }, (item: string) => item)
146    }.cachedCount(5)
147  }
148}
149```
150
151In the preceding code snippets, the key generation rule is the return value **item** of the **keyGenerator** function. During loop rendering, **LazyForEach** generates keys in the sequence of **Hello 0**, **Hello 1**, ..., **Hello 20** for the array item of the data source, creates the corresponding **ListItem** child components and render them on the GUI.
152
153The figure below shows the effect.
154
155**Figure 1** Initial render of LazyForEach 
156![LazyForEach-Render-DifferentKey](./figures/LazyForEach-Render-DifferentKey.gif)
157
158- ### Incorrect Rendering When Keys Are the Same
159
160When the keys generated for different data items are the same, the behavior of the framework is unpredictable. For example, in the following code, the keys of the data items rendered by **LazyForEach** are the same. During the swipe process, **LazyForEach** preloads child components for the current page. Because the new child component and the destroyed component have the same key, the framework may incorrectly obtain the cache. As a result, the child component rendering is abnormal.
161
162 ```ts
163class BasicDataSource implements IDataSource {
164  private listeners: DataChangeListener[] = [];
165  private originDataArray: string[] = [];
166
167  public totalCount(): number {
168    return 0;
169  }
170
171  public getData(index: number): string {
172    return this.originDataArray[index];
173  }
174
175  registerDataChangeListener(listener: DataChangeListener): void {
176    if (this.listeners.indexOf(listener) < 0) {
177      console.info('add listener');
178      this.listeners.push(listener);
179    }
180  }
181
182  unregisterDataChangeListener(listener: DataChangeListener): void {
183    const pos = this.listeners.indexOf(listener);
184    if (pos >= 0) {
185      console.info('remove listener');
186      this.listeners.splice(pos, 1);
187    }
188  }
189
190  notifyDataReload(): void {
191    this.listeners.forEach(listener => {
192      listener.onDataReloaded();
193    })
194  }
195
196  notifyDataAdd(index: number): void {
197    this.listeners.forEach(listener => {
198      listener.onDataAdd(index);
199    })
200  }
201
202  notifyDataChange(index: number): void {
203    this.listeners.forEach(listener => {
204      listener.onDataChange(index);
205    })
206  }
207
208  notifyDataDelete(index: number): void {
209    this.listeners.forEach(listener => {
210      listener.onDataDelete(index);
211    })
212  }
213
214  notifyDataMove(from: number, to: number): void {
215    this.listeners.forEach(listener => {
216      listener.onDataMove(from, to);
217    })
218  }
219}
220
221class MyDataSource extends BasicDataSource {
222  private dataArray: string[] = [];
223
224  public totalCount(): number {
225    return this.dataArray.length;
226  }
227
228  public getData(index: number): string {
229    return this.dataArray[index];
230  }
231
232  public addData(index: number, data: string): void {
233    this.dataArray.splice(index, 0, data);
234    this.notifyDataAdd(index);
235  }
236
237  public pushData(data: string): void {
238    this.dataArray.push(data);
239    this.notifyDataAdd(this.dataArray.length - 1);
240  }
241}
242
243@Entry
244@Component
245struct MyComponent {
246  private data: MyDataSource = new MyDataSource();
247
248  aboutToAppear() {
249    for (let i = 0; i <= 20; i++) {
250      this.data.pushData(`Hello ${i}`)
251    }
252  }
253
254  build() {
255    List({ space: 3 }) {
256      LazyForEach(this.data, (item: string) => {
257        ListItem() {
258          Row() {
259            Text(item).fontSize(50)
260              .onAppear(() => {
261                console.info("appear:" + item)
262              })
263          }.margin({ left: 10, right: 10 })
264        }
265      }, (item: string) => 'same key')
266    }.cachedCount(5)
267  }
268}
269 ```
270
271The figure below shows the effect.
272
273**Figure 2** LazyForEach rendering when keys are the same 
274![LazyForEach-Render-SameKey](./figures/LazyForEach-Render-SameKey.gif)
275
276### Non Initial Render
277
278When the **LazyForEach** data source changes and a re-render is required, call a listener API based on the data source change to notify **LazyForEach**. Below are some use cases.
279
280- ### Adding Data
281
282```ts
283class BasicDataSource implements IDataSource {
284  private listeners: DataChangeListener[] = [];
285  private originDataArray: string[] = [];
286
287  public totalCount(): number {
288    return 0;
289  }
290
291  public getData(index: number): string {
292    return this.originDataArray[index];
293  }
294
295  registerDataChangeListener(listener: DataChangeListener): void {
296    if (this.listeners.indexOf(listener) < 0) {
297      console.info('add listener');
298      this.listeners.push(listener);
299    }
300  }
301
302  unregisterDataChangeListener(listener: DataChangeListener): void {
303    const pos = this.listeners.indexOf(listener);
304    if (pos >= 0) {
305      console.info('remove listener');
306      this.listeners.splice(pos, 1);
307    }
308  }
309
310  notifyDataReload(): void {
311    this.listeners.forEach(listener => {
312      listener.onDataReloaded();
313    })
314  }
315
316  notifyDataAdd(index: number): void {
317    this.listeners.forEach(listener => {
318      listener.onDataAdd(index);
319      // Method 2: listener.onDatasetChange([{type: DataOperationType.ADD, index: index}]);
320    })
321  }
322
323  notifyDataChange(index: number): void {
324    this.listeners.forEach(listener => {
325      listener.onDataChange(index);
326    })
327  }
328
329  notifyDataDelete(index: number): void {
330    this.listeners.forEach(listener => {
331      listener.onDataDelete(index);
332    })
333  }
334
335  notifyDataMove(from: number, to: number): void {
336    this.listeners.forEach(listener => {
337      listener.onDataMove(from, to);
338    })
339  }
340}
341
342class MyDataSource extends BasicDataSource {
343  private dataArray: string[] = [];
344
345  public totalCount(): number {
346    return this.dataArray.length;
347  }
348
349  public getData(index: number): string {
350    return this.dataArray[index];
351  }
352
353  public addData(index: number, data: string): void {
354    this.dataArray.splice(index, 0, data);
355    this.notifyDataAdd(index);
356  }
357
358  public pushData(data: string): void {
359    this.dataArray.push(data);
360    this.notifyDataAdd(this.dataArray.length - 1);
361  }
362}
363
364@Entry
365@Component
366struct MyComponent {
367  private data: MyDataSource = new MyDataSource();
368
369  aboutToAppear() {
370    for (let i = 0; i <= 20; i++) {
371      this.data.pushData(`Hello ${i}`)
372    }
373  }
374
375  build() {
376    List({ space: 3 }) {
377      LazyForEach(this.data, (item: string) => {
378        ListItem() {
379          Row() {
380            Text(item).fontSize(50)
381              .onAppear(() => {
382                console.info("appear:" + item)
383              })
384          }.margin({ left: 10, right: 10 })
385        }
386        .onClick(() => {
387          // Click to add a child component.
388          this.data.pushData(`Hello ${this.data.totalCount()}`);
389        })
390      }, (item: string) => item)
391    }.cachedCount(5)
392  }
393}
394```
395
396When the child component of **LazyForEach** is clicked, the **pushData** method of the data source is called first. This method adds data to the end of the data source and then calls the **notifyDataAdd** method. In the **notifyDataAdd** method, the **listener.onDataAdd** method is called to notify **LazyForEach** that data is added, and LazyForEach creates a child component at the position indicated by the specified index.
397
398The figure below shows the effect.
399
400**Figure 3** Adding data to LazyForEach 
401![LazyForEach-Add-Data](./figures/LazyForEach-Add-Data.gif)
402
403- ### Deleting Data
404
405```ts
406class BasicDataSource implements IDataSource {
407  private listeners: DataChangeListener[] = [];
408  private originDataArray: string[] = [];
409
410  public totalCount(): number {
411    return 0;
412  }
413
414  public getData(index: number): string {
415    return this.originDataArray[index];
416  }
417
418  registerDataChangeListener(listener: DataChangeListener): void {
419    if (this.listeners.indexOf(listener) < 0) {
420      console.info('add listener');
421      this.listeners.push(listener);
422    }
423  }
424
425  unregisterDataChangeListener(listener: DataChangeListener): void {
426    const pos = this.listeners.indexOf(listener);
427    if (pos >= 0) {
428      console.info('remove listener');
429      this.listeners.splice(pos, 1);
430    }
431  }
432
433  notifyDataReload(): void {
434    this.listeners.forEach(listener => {
435      listener.onDataReloaded();
436    })
437  }
438
439  notifyDataAdd(index: number): void {
440    this.listeners.forEach(listener => {
441      listener.onDataAdd(index);
442    })
443  }
444
445  notifyDataChange(index: number): void {
446    this.listeners.forEach(listener => {
447      listener.onDataChange(index);
448    })
449  }
450
451  notifyDataDelete(index: number): void {
452    this.listeners.forEach(listener => {
453      listener.onDataDelete(index);
454      // Method 2: listener.onDatasetChange([{type: DataOperationType.DELETE, index: index}]);
455    })
456  }
457
458  notifyDataMove(from: number, to: number): void {
459    this.listeners.forEach(listener => {
460      listener.onDataMove(from, to);
461    })
462  }
463}
464
465class MyDataSource extends BasicDataSource {
466  dataArray: string[] = [];
467
468  public totalCount(): number {
469    return this.dataArray.length;
470  }
471
472  public getData(index: number): string {
473    return this.dataArray[index];
474  }
475
476  public addData(index: number, data: string): void {
477    this.dataArray.splice(index, 0, data);
478    this.notifyDataAdd(index);
479  }
480
481  public pushData(data: string): void {
482    this.dataArray.push(data);
483  }
484  
485  public deleteData(index: number): void {
486    this.dataArray.splice(index, 1);
487    this.notifyDataDelete(index);
488  }
489}
490
491@Entry
492@Component
493struct MyComponent {
494  private data: MyDataSource = new MyDataSource();
495
496  aboutToAppear() {
497    for (let i = 0; i <= 20; i++) {
498      this.data.pushData(`Hello ${i}`)
499    }
500  }
501
502  build() {
503    List({ space: 3 }) {
504      LazyForEach(this.data, (item: string, index: number) => {
505        ListItem() {
506          Row() {
507            Text(item).fontSize(50)
508              .onAppear(() => {
509                console.info("appear:" + item)
510              })
511          }.margin({ left: 10, right: 10 })
512        }
513        .onClick(() => {
514          // Click to delete a child component.
515          this.data.deleteData(this.data.dataArray.indexOf(item));
516        })
517      }, (item: string) => item)
518    }.cachedCount(5)
519  }
520}
521```
522
523When the child component of **LazyForEach** is clicked, the **deleteData** method of the data source is called first. This method deletes data that matches the specified index from the data source and then calls the **notifyDataDelete** method. In the **notifyDataDelete** method, the **listener.onDataDelete** method is called to notify **LazyForEach** that data is deleted, and **LazyForEach** deletes the child component at the position indicated by the specified index.
524
525The figure below shows the effect.
526
527**Figure 4** Deleting data from LazyForEach 
528![LazyForEach-Delete-Data](./figures/LazyForEach-Delete-Data.gif)
529
530- ### Swapping Data
531
532```ts
533class BasicDataSource implements IDataSource {
534  private listeners: DataChangeListener[] = [];
535  private originDataArray: string[] = [];
536
537  public totalCount(): number {
538    return 0;
539  }
540
541  public getData(index: number): string {
542    return this.originDataArray[index];
543  }
544
545  registerDataChangeListener(listener: DataChangeListener): void {
546    if (this.listeners.indexOf(listener) < 0) {
547      console.info('add listener');
548      this.listeners.push(listener);
549    }
550  }
551
552  unregisterDataChangeListener(listener: DataChangeListener): void {
553    const pos = this.listeners.indexOf(listener);
554    if (pos >= 0) {
555      console.info('remove listener');
556      this.listeners.splice(pos, 1);
557    }
558  }
559
560  notifyDataReload(): void {
561    this.listeners.forEach(listener => {
562      listener.onDataReloaded();
563    })
564  }
565
566  notifyDataAdd(index: number): void {
567    this.listeners.forEach(listener => {
568      listener.onDataAdd(index);
569    })
570  }
571
572  notifyDataChange(index: number): void {
573    this.listeners.forEach(listener => {
574      listener.onDataChange(index);
575    })
576  }
577
578  notifyDataDelete(index: number): void {
579    this.listeners.forEach(listener => {
580      listener.onDataDelete(index);
581    })
582  }
583
584  notifyDataMove(from: number, to: number): void {
585    this.listeners.forEach(listener => {
586      listener.onDataMove(from, to);
587      // Method 2: listener.onDatasetChange ()
588      //         [{type: DataOperationType.EXCHANGE, index: {start: from, end: to}}]);
589    })
590  }
591}
592
593class MyDataSource extends BasicDataSource {
594  dataArray: string[] = [];
595
596  public totalCount(): number {
597    return this.dataArray.length;
598  }
599
600  public getData(index: number): string {
601    return this.dataArray[index];
602  }
603
604  public addData(index: number, data: string): void {
605    this.dataArray.splice(index, 0, data);
606    this.notifyDataAdd(index);
607  }
608
609  public pushData(data: string): void {
610    this.dataArray.push(data);
611  }
612  
613  public deleteData(index: number): void {
614    this.dataArray.splice(index, 1);
615    this.notifyDataDelete(index);
616  }
617  
618  public moveData(from: number, to: number): void {
619    let temp: string = this.dataArray[from];
620    this.dataArray[from] = this.dataArray[to];
621    this.dataArray[to] = temp;
622    this.notifyDataMove(from, to);
623  }
624}
625
626@Entry
627@Component
628struct MyComponent {
629  private moved: number[] = [];
630  private data: MyDataSource = new MyDataSource();
631
632  aboutToAppear() {
633    for (let i = 0; i <= 20; i++) {
634      this.data.pushData(`Hello ${i}`)
635    }
636  }
637
638  build() {
639    List({ space: 3 }) {
640      LazyForEach(this.data, (item: string, index: number) => {
641        ListItem() {
642          Row() {
643            Text(item).fontSize(50)
644              .onAppear(() => {
645                console.info("appear:" + item)
646              })
647          }.margin({ left: 10, right: 10 })
648        }
649        .onClick(() => {
650          this.moved.push(this.data.dataArray.indexOf(item));
651          if (this.moved.length === 2) {
652          	// Click to exchange child components.
653          	this.data.moveData(this.moved[0], this.moved[1]);
654            this.moved = [];
655          }
656        })
657      }, (item: string) => item)
658    }.cachedCount(5)
659  }
660}
661```
662
663When a child component of **LazyForEach** is clicked, the index of the data to be moved is stored in the **moved** member variable. When another child component of **LazyForEach** is clicked, the first child component clicked is moved here. The **moveData** method of the data source is called to move the data from the original location to the expected location, after which the **notifyDataMove** method is called. In the **notifyDataMove** method, the **listener.onDataMove** method is called to notify **LazyForEach** that data needs to be moved.** LazyForEach** then swaps data between the **from** and **to** positions.
664
665The figure below shows the effect.
666
667**Figure 5** Swapping data in LazyForEach 
668![LazyForEach-Exchange-Data](./figures/LazyForEach-Exchange-Data.gif)
669
670- ### Changing a Data Item
671
672```ts
673class BasicDataSource implements IDataSource {
674  private listeners: DataChangeListener[] = [];
675  private originDataArray: string[] = [];
676
677  public totalCount(): number {
678    return 0;
679  }
680
681  public getData(index: number): string {
682    return this.originDataArray[index];
683  }
684
685  registerDataChangeListener(listener: DataChangeListener): void {
686    if (this.listeners.indexOf(listener) < 0) {
687      console.info('add listener');
688      this.listeners.push(listener);
689    }
690  }
691
692  unregisterDataChangeListener(listener: DataChangeListener): void {
693    const pos = this.listeners.indexOf(listener);
694    if (pos >= 0) {
695      console.info('remove listener');
696      this.listeners.splice(pos, 1);
697    }
698  }
699
700  notifyDataReload(): void {
701    this.listeners.forEach(listener => {
702      listener.onDataReloaded();
703    })
704  }
705
706  notifyDataAdd(index: number): void {
707    this.listeners.forEach(listener => {
708      listener.onDataAdd(index);
709    })
710  }
711
712  notifyDataChange(index: number): void {
713    this.listeners.forEach(listener => {
714      listener.onDataChange(index);
715      // Method 2: listener.onDatasetChange([{type: DataOperationType.CHANGE, index: index}]);
716    })
717  }
718
719  notifyDataDelete(index: number): void {
720    this.listeners.forEach(listener => {
721      listener.onDataDelete(index);
722    })
723  }
724
725  notifyDataMove(from: number, to: number): void {
726    this.listeners.forEach(listener => {
727      listener.onDataMove(from, to);
728    })
729  }
730}
731
732class MyDataSource extends BasicDataSource {
733  private dataArray: string[] = [];
734
735  public totalCount(): number {
736    return this.dataArray.length;
737  }
738
739  public getData(index: number): string {
740    return this.dataArray[index];
741  }
742
743  public addData(index: number, data: string): void {
744    this.dataArray.splice(index, 0, data);
745    this.notifyDataAdd(index);
746  }
747
748  public pushData(data: string): void {
749    this.dataArray.push(data);
750  }
751  
752  public deleteData(index: number): void {
753    this.dataArray.splice(index, 1);
754    this.notifyDataDelete(index);
755  }
756  
757  public changeData(index: number, data: string): void {
758    this.dataArray.splice(index, 1, data);
759    this.notifyDataChange(index);
760  }
761}
762
763@Entry
764@Component
765struct MyComponent {
766  private moved: number[] = [];
767  private data: MyDataSource = new MyDataSource();
768
769  aboutToAppear() {
770    for (let i = 0; i <= 20; i++) {
771      this.data.pushData(`Hello ${i}`)
772    }
773  }
774
775
776  build() {
777    List({ space: 3 }) {
778      LazyForEach(this.data, (item: string, index: number) => {
779        ListItem() {
780          Row() {
781            Text(item).fontSize(50)
782              .onAppear(() => {
783                console.info("appear:" + item)
784              })
785          }.margin({ left: 10, right: 10 })
786        }
787        .onClick(() => {
788          this.data.changeData(index, item + '00');
789        })
790      }, (item: string) => item)
791    }.cachedCount(5)
792  }
793}
794```
795
796When the child component of **LazyForEach** is clicked, the data is changed first, and then the **changeData** method of the data source is called. In this method, the **notifyDataChange** method is called. In the **notifyDataChange** method, the **listener.onDataChange** method is called to notify **LazyForEach** of data changes. **LazyForEach** then rebuilds the child component that matches the specified index.
797
798The figure below shows the effect.
799
800**Figure 6** Changing a data item in LazyForEach 
801![LazyForEach-Change-SingleData](./figures/LazyForEach-Change-SingleData.gif)
802
803- ### Changing Multiple Data Items
804
805```ts
806class BasicDataSource implements IDataSource {
807  private listeners: DataChangeListener[] = [];
808  private originDataArray: string[] = [];
809
810  public totalCount(): number {
811    return 0;
812  }
813
814  public getData(index: number): string {
815    return this.originDataArray[index];
816  }
817
818  registerDataChangeListener(listener: DataChangeListener): void {
819    if (this.listeners.indexOf(listener) < 0) {
820      console.info('add listener');
821      this.listeners.push(listener);
822    }
823  }
824
825  unregisterDataChangeListener(listener: DataChangeListener): void {
826    const pos = this.listeners.indexOf(listener);
827    if (pos >= 0) {
828      console.info('remove listener');
829      this.listeners.splice(pos, 1);
830    }
831  }
832
833  notifyDataReload(): void {
834    this.listeners.forEach(listener => {
835      listener.onDataReloaded();
836      // Method 2: listener.onDatasetChange([{type: DataOperationType.RELOAD}]);
837    })
838  }
839
840  notifyDataAdd(index: number): void {
841    this.listeners.forEach(listener => {
842      listener.onDataAdd(index);
843    })
844  }
845
846  notifyDataChange(index: number): void {
847    this.listeners.forEach(listener => {
848      listener.onDataChange(index);
849    })
850  }
851
852  notifyDataDelete(index: number): void {
853    this.listeners.forEach(listener => {
854      listener.onDataDelete(index);
855    })
856  }
857
858  notifyDataMove(from: number, to: number): void {
859    this.listeners.forEach(listener => {
860      listener.onDataMove(from, to);
861    })
862  }
863}
864
865class MyDataSource extends BasicDataSource {
866  private dataArray: string[] = [];
867
868  public totalCount(): number {
869    return this.dataArray.length;
870  }
871
872  public getData(index: number): string {
873    return this.dataArray[index];
874  }
875
876  public addData(index: number, data: string): void {
877    this.dataArray.splice(index, 0, data);
878    this.notifyDataAdd(index);
879  }
880
881  public pushData(data: string): void {
882    this.dataArray.push(data);
883  }
884  
885  public deleteData(index: number): void {
886    this.dataArray.splice(index, 1);
887    this.notifyDataDelete(index);
888  }
889  
890  public changeData(index: number): void {
891    this.notifyDataChange(index);
892  }
893    
894  public reloadData(): void {
895    this.notifyDataReload();
896  }
897    
898  public modifyAllData(): void {
899    this.dataArray = this.dataArray.map((item: string) => {
900        return item + '0';
901    })
902  }
903}
904
905@Entry
906@Component
907struct MyComponent {
908  private moved: number[] = [];
909  private data: MyDataSource = new MyDataSource();
910
911  aboutToAppear() {
912    for (let i = 0; i <= 20; i++) {
913      this.data.pushData(`Hello ${i}`)
914    }
915  }
916
917  build() {
918    List({ space: 3 }) {
919      LazyForEach(this.data, (item: string, index: number) => {
920        ListItem() {
921          Row() {
922            Text(item).fontSize(50)
923              .onAppear(() => {
924                console.info("appear:" + item)
925              })
926          }.margin({ left: 10, right: 10 })
927        }
928        .onClick(() => {
929          this.data.modifyAllData();
930          this.data.reloadData();
931        })
932      }, (item: string) => item)
933    }.cachedCount(5)
934  }
935}
936```
937
938When a child component of **LazyForEach** is clicked, the **modifyAllData** method of the data source is called to change all data items, and then the **reloadData** method of the data source is called. In this method, the **notifyDataReload** method is called. In the **notifyDataReload** method, the **listener.onDataReloaded** method is called to notify **LazyForEach** that all subnodes need to be rebuilt. **LazyForEach** compares the keys of all original data items with those of all new data items on a one-by-one basis. If the keys are the same, the cache is used. If the keys are different, the child component is rebuilt.
939
940The figure below shows the effect.
941
942**Figure 7** Changing multiple data items in LazyForEach 
943![LazyForEach-Reload-Data](./figures/LazyForEach-Reload-Data.gif)
944
945- ### Precisely Changing Data in Batches
946
947```ts
948class BasicDataSource implements IDataSource {
949  private listeners: DataChangeListener[] = [];
950  private originDataArray: string[] = [];
951
952  public totalCount(): number {
953    return 0;
954  }
955
956  public getData(index: number): string {
957    return this.originDataArray[index];
958  }
959
960  registerDataChangeListener(listener: DataChangeListener): void {
961    if (this.listeners.indexOf(listener) < 0) {
962      console.info('add listener');
963      this.listeners.push(listener);
964    }
965  }
966
967  unregisterDataChangeListener(listener: DataChangeListener): void {
968    const pos = this.listeners.indexOf(listener);
969    if (pos >= 0) {
970      console.info('remove listener');
971      this.listeners.splice(pos, 1);
972    }
973  }
974
975  notifyDatasetChange(operations: DataOperation[]): void {
976    this.listeners.forEach(listener => {
977      listener.onDatasetChange(operations);
978    })
979  }
980}
981
982class MyDataSource extends BasicDataSource {
983  private dataArray: string[] = [];
984
985  public totalCount(): number {
986    return this.dataArray.length;
987  }
988
989  public getData(index: number): string {
990    return this.dataArray[index];
991  }
992
993  public operateData(): void {
994    console.info(JSON.stringify(this.dataArray));
995    this.dataArray.splice(4, 0, this.dataArray[1]);
996    this.dataArray.splice(1, 1);
997    let temp = this.dataArray[4];
998    this.dataArray[4] = this.dataArray[6];
999    this.dataArray[6] = temp
1000    this.dataArray.splice(8, 0, 'Hello 1', 'Hello 2');
1001    this.dataArray.splice(12, 2);
1002    console.info(JSON.stringify(this.dataArray));
1003    this.notifyDatasetChange([
1004      { type: DataOperationType.MOVE, index: { from: 1, to: 3 } },
1005      { type: DataOperationType.EXCHANGE, index: { start: 4, end: 6 } },
1006      { type: DataOperationType.ADD, index: 8, count: 2 },
1007      { type: DataOperationType.DELETE, index: 10, count: 2 }]);
1008  }
1009
1010  public init(): void {
1011    this.dataArray.splice(0, 0, 'Hello a', 'Hello b', 'Hello c', 'Hello d', 'Hello e', 'Hello f', 'Hello g', 'Hello h',
1012      'Hello i', 'Hello j', 'Hello k', 'Hello l', 'Hello m', 'Hello n', 'Hello o', 'Hello p', 'Hello q', 'Hello r');
1013  }
1014}
1015
1016@Entry
1017@Component
1018struct MyComponent {
1019  private data: MyDataSource = new MyDataSource();
1020
1021  aboutToAppear() {
1022    this.data.init()
1023  }
1024
1025  build() {
1026    Column() {
1027      Text('Move the second item to where the fourth item is located, exchange the fifth and seventh data items, add "Hello 1" "Hello 2" to the ninth item, and delete two items starting from the eleventh item')
1028        .fontSize(10)
1029        .backgroundColor(Color.Blue)
1030        .fontColor(Color.White)
1031        .borderRadius(50)
1032        .padding(5)
1033        .onClick(() => {
1034          this.data.operateData();
1035        })
1036      List({ space: 3 }) {
1037        LazyForEach(this.data, (item: string, index: number) => {
1038          ListItem() {
1039            Row() {
1040              Text(item).fontSize(35)
1041                .onAppear(() => {
1042                  console.info("appear:" + item)
1043                })
1044            }.margin({ left: 10, right: 10 })
1045          }
1046
1047        }, (item: string) => item + new Date().getTime())
1048      }.cachedCount(5)
1049    }
1050  }
1051}
1052```
1053
1054The **onDatasetChange** API notifies **LazyForEach** of the operations to be performed at once. In the preceding example, **LazyForEach** adds, deletes, moves, and exchanges data at the same time. 
1055
1056**Figure 8** Changing multiple data items in LazyForEach 
1057
1058![LazyForEach-Change-MultiData](./figures/LazyForEach-Change-MultiData.gif)  
1059
1060In the second example, values are directly changed in the array without using **splice()**. Result of **operations** is directly obtained by comparing the original array with the new array.
1061```ts
1062class BasicDataSource implements IDataSource {
1063  private listeners: DataChangeListener[] = [];
1064  private originDataArray: string[] = [];
1065
1066  public totalCount(): number {
1067    return 0;
1068  }
1069
1070  public getData(index: number): string {
1071    return this.originDataArray[index];
1072  }
1073
1074  registerDataChangeListener(listener: DataChangeListener): void {
1075    if (this.listeners.indexOf(listener) < 0) {
1076      console.info('add listener');
1077      this.listeners.push(listener);
1078    }
1079  }
1080
1081  unregisterDataChangeListener(listener: DataChangeListener): void {
1082    const pos = this.listeners.indexOf(listener);
1083    if (pos >= 0) {
1084      console.info('remove listener');
1085      this.listeners.splice(pos, 1);
1086    }
1087  }
1088
1089  notifyDatasetChange(operations: DataOperation[]): void {
1090    this.listeners.forEach(listener => {
1091      listener.onDatasetChange(operations);
1092    })
1093  }
1094}
1095
1096class MyDataSource extends BasicDataSource {
1097  private dataArray: string[] = [];
1098
1099  public totalCount(): number {
1100    return this.dataArray.length;
1101  }
1102
1103  public getData(index: number): string {
1104    return this.dataArray[index];
1105  }
1106
1107  public operateData(): void {
1108    this.dataArray =
1109      ['Hello x', 'Hello 1', 'Hello 2', 'Hello b', 'Hello c', 'Hello e', 'Hello d', 'Hello f', 'Hello g', 'Hello h']
1110    this.notifyDatasetChange([
1111      { type: DataOperationType.CHANGE, index: 0 },
1112      { type: DataOperationType.ADD, index: 1, count: 2 },
1113      { type: DataOperationType.EXCHANGE, index: { start: 3, end: 4 } },
1114    ]);
1115  }
1116
1117  public init(): void {
1118    this.dataArray = ['Hello a', 'Hello b', 'Hello c', 'Hello d', 'Hello e', 'Hello f', 'Hello g', 'Hello h'];
1119  }
1120}
1121
1122@Entry
1123@Component
1124struct MyComponent {
1125  private data: MyDataSource = new MyDataSource();
1126
1127  aboutToAppear() {
1128    this.data.init()
1129  }
1130
1131  build() {
1132    Column() {
1133      Text('Multi-Data Change')
1134        .fontSize(10)
1135        .backgroundColor(Color.Blue)
1136        .fontColor(Color.White)
1137        .borderRadius(50)
1138        .padding(5)
1139        .onClick(() => {
1140          this.data.operateData();
1141        })
1142      List({ space: 3 }) {
1143        LazyForEach(this.data, (item: string, index: number) => {
1144          ListItem() {
1145            Row() {
1146              Text(item).fontSize(35)
1147                .onAppear(() => {
1148                  console.info("appear:" + item)
1149                })
1150            }.margin({ left: 10, right: 10 })
1151          }
1152
1153        }, (item: string) => item + new Date().getTime())
1154      }.cachedCount(5)
1155    }
1156  }
1157}
1158```
1159**Figure 9** Changing multiple data items in LazyForEach
1160
1161![LazyForEach-Change-MultiData2](./figures/LazyForEach-Change-MultiData2.gif)  
1162
1163Pay attention to the following when using the **onDatasetChange** API:
1164
11651. The **onDatasetChange** API cannot be used together with other data operation APIs.
11662. The input parameter of **onDatasetChange** is operations. The **index** of each operation is sourced from the original array. Therefore, the index in operations(input parameter of **onDatasetChange**) does not correspond exactly with the index in the operations on Datasource and is not negative.   
1167The first example clearly illustrates this point:
1168```ts
1169// Array before modification.
1170["Hello a","Hello b","Hello c","Hello d","Hello e","Hello f","Hello g","Hello h","Hello i","Hello j","Hello k","Hello l","Hello m","Hello n","Hello o","Hello p","Hello q","Hello r"]
1171//Array after modification.
1172["Hello a","Hello c","Hello d","Hello b","Hello g","Hello f","Hello e","Hello h","Hello 1","Hello 2","Hello i","Hello j","Hello m","Hello n","Hello o","Hello p","Hello q","Hello r"]
1173```
1174**Hello b** is changed from item 2 to item 4. Therefore, the first **operation** is written in **{ type: DataOperationType.MOVE, index: { from: 1, to: 3 } }**. 
1175**Hello e** whose index is 4 and **Hello g** whose index is 6 are exchanged in the original array. Therefore, the second **operation** is written in **{ type: DataOperationType.EXCHANGE, index: { start: 4, end: 6 } }**. 
1176**Hello 1** and **Hello 2** are inserted after **Hello h** whose index is 7 in the original array. Therefore, the third **operation** is written in **{ type: DataOperationType.ADD, index: 8, count: 2 }**. 
1177**Hello k** whose index is 10 and **Hello l** whose index is 11 are deleted in the original array. Therefore, the fourth **operation** is written in **{ type: DataOperationType.DELETE, index: 10, count: 2 }**. 
1178
11793. When **onDatasetChange** is called, the data can be operated only once for each index. If the data is operated multiple times, **LazyForEach** enables only the first operation to take effect.
11804. In operations where you can specify keys on your own, **LazyForEach** does not call the key generator to obtain keys. As such, make sure the specified keys are correct.
11815. If the API contains the **RELOAD** operation, other operations do not take effect.
1182
1183
1184
1185- ### Changing Data Subproperties
1186
1187When **LazyForEach** is used for UI re-renders, a child component needs to be destroyed and rebuilt when the data item changes. This may result in low re-render performance when the child component structure is complex. This is where @Observed and @ObjectLink come into picture. By providing in-depth observation, @Observed and @ObjectLink enable precise re-renders of only components that use the changed properties. You can select a re-render mode that better suits your needs.
1188
1189```ts
1190class BasicDataSource implements IDataSource {
1191  private listeners: DataChangeListener[] = [];
1192  private originDataArray: StringData[] = [];
1193
1194  public totalCount(): number {
1195    return 0;
1196  }
1197
1198  public getData(index: number): StringData {
1199    return this.originDataArray[index];
1200  }
1201
1202  registerDataChangeListener(listener: DataChangeListener): void {
1203    if (this.listeners.indexOf(listener) < 0) {
1204      console.info('add listener');
1205      this.listeners.push(listener);
1206    }
1207  }
1208
1209  unregisterDataChangeListener(listener: DataChangeListener): void {
1210    const pos = this.listeners.indexOf(listener);
1211    if (pos >= 0) {
1212      console.info('remove listener');
1213      this.listeners.splice(pos, 1);
1214    }
1215  }
1216
1217  notifyDataReload(): void {
1218    this.listeners.forEach(listener => {
1219      listener.onDataReloaded();
1220    })
1221  }
1222
1223  notifyDataAdd(index: number): void {
1224    this.listeners.forEach(listener => {
1225      listener.onDataAdd(index);
1226    })
1227  }
1228
1229  notifyDataChange(index: number): void {
1230    this.listeners.forEach(listener => {
1231      listener.onDataChange(index);
1232    })
1233  }
1234
1235  notifyDataDelete(index: number): void {
1236    this.listeners.forEach(listener => {
1237      listener.onDataDelete(index);
1238    })
1239  }
1240
1241  notifyDataMove(from: number, to: number): void {
1242    this.listeners.forEach(listener => {
1243      listener.onDataMove(from, to);
1244    })
1245  }
1246}
1247
1248class MyDataSource extends BasicDataSource {
1249  private dataArray: StringData[] = [];
1250
1251  public totalCount(): number {
1252    return this.dataArray.length;
1253  }
1254
1255  public getData(index: number): StringData {
1256    return this.dataArray[index];
1257  }
1258
1259  public addData(index: number, data: StringData): void {
1260    this.dataArray.splice(index, 0, data);
1261    this.notifyDataAdd(index);
1262  }
1263
1264  public pushData(data: StringData): void {
1265    this.dataArray.push(data);
1266    this.notifyDataAdd(this.dataArray.length - 1);
1267  }
1268}
1269
1270@Observed
1271class StringData {
1272  message: string;
1273  constructor(message: string) {
1274    this.message = message;
1275  }  
1276}
1277
1278@Entry
1279@Component
1280struct MyComponent {
1281  private moved: number[] = [];
1282  @State data: MyDataSource = new MyDataSource();
1283
1284  aboutToAppear() {
1285    for (let i = 0; i <= 20; i++) {
1286      this.data.pushData(new StringData(`Hello ${i}`));
1287    }
1288  }
1289
1290  build() {
1291    List({ space: 3 }) {
1292      LazyForEach(this.data, (item: StringData, index: number) => {
1293        ListItem() {
1294          ChildComponent({data: item})
1295        }
1296        .onClick(() => {
1297          item.message += '0';
1298        })
1299      }, (item: StringData, index: number) => index.toString())
1300    }.cachedCount(5)
1301  }
1302}
1303
1304@Component
1305struct ChildComponent {
1306  @ObjectLink data: StringData
1307  build() {
1308    Row() {
1309      Text(this.data.message).fontSize(50)
1310        .onAppear(() => {
1311          console.info("appear:" + this.data.message)
1312        })
1313    }.margin({ left: 10, right: 10 })
1314  }
1315}
1316```
1317
1318When the child component of **LazyForEach** is clicked, **item.message** is changed. As re-rendering depends on the listening of the @ObjectLink decorated member variable of **ChildComponent** on its subproperties. In this case, the framework only re-renders **Text(this.data.message)** and does not rebuild the entire **ListItem** child component.
1319
1320**Figure 10** Changing data subproperties in LazyForEach 
1321![LazyForEach-Change-SubProperty](./figures/LazyForEach-Change-SubProperty.gif)
1322
1323## Enabling Drag and Sort
1324If **LazyForEach** is used in a list, and the **onMove** event is set, you can enable drag and sort for the list items. If an item changes the position after you drag and sort the data, the **onMove** event is triggered to report the original index and target index of the item. The data source needs to be modified in the **onMove** event based on the reported start index and target index. The **DataChangeListener** API does not need to be called to notify the data source change.
1325
1326```ts
1327class BasicDataSource implements IDataSource {
1328  private listeners: DataChangeListener[] = [];
1329  private originDataArray: string[] = [];
1330
1331  public totalCount(): number {
1332    return 0;
1333  }
1334
1335  public getData(index: number): string {
1336    return this.originDataArray[index];
1337  }
1338
1339  registerDataChangeListener(listener: DataChangeListener): void {
1340    if (this.listeners.indexOf(listener) < 0) {
1341      console.info('add listener');
1342      this.listeners.push(listener);
1343    }
1344  }
1345
1346  unregisterDataChangeListener(listener: DataChangeListener): void {
1347    const pos = this.listeners.indexOf(listener);
1348    if (pos >= 0) {
1349      console.info('remove listener');
1350      this.listeners.splice(pos, 1);
1351    }
1352  }
1353
1354  notifyDataReload(): void {
1355    this.listeners.forEach(listener => {
1356      listener.onDataReloaded();
1357    })
1358  }
1359
1360  notifyDataAdd(index: number): void {
1361    this.listeners.forEach(listener => {
1362      listener.onDataAdd(index);
1363    })
1364  }
1365
1366  notifyDataChange(index: number): void {
1367    this.listeners.forEach(listener => {
1368      listener.onDataChange(index);
1369    })
1370  }
1371
1372  notifyDataDelete(index: number): void {
1373    this.listeners.forEach(listener => {
1374      listener.onDataDelete(index);
1375    })
1376  }
1377
1378  notifyDataMove(from: number, to: number): void {
1379    this.listeners.forEach(listener => {
1380      listener.onDataMove(from, to);
1381    })
1382  }
1383}
1384
1385class MyDataSource extends BasicDataSource {
1386  private dataArray: string[] = [];
1387
1388  public totalCount(): number {
1389    return this.dataArray.length;
1390  }
1391
1392  public getData(index: number): string {
1393    return this.dataArray[index];
1394  }
1395
1396  public addData(index: number, data: string): void {
1397    this.dataArray.splice(index, 0, data);
1398    this.notifyDataAdd(index);
1399  }
1400
1401  public moveDataWithoutNotify(from: number, to: number): void {
1402    let tmp = this.dataArray.splice(from, 1);
1403    this.dataArray.splice(to, 0, tmp[0])
1404  }
1405
1406  public pushData(data: string): void {
1407    this.dataArray.push(data);
1408    this.notifyDataAdd(this.dataArray.length - 1);
1409  }
1410
1411  public deleteData(index: number): void {
1412    this.dataArray.splice(index, 1);
1413    this.notifyDataDelete(index);
1414  }
1415}
1416
1417@Entry
1418@Component
1419struct Parent {
1420  private data: MyDataSource = new MyDataSource();
1421
1422  build() {
1423    Row() {
1424      List() {
1425        LazyForEach(this.data, (item: string) => {
1426            ListItem() {
1427              Text(item.toString())
1428                .fontSize(16)
1429                .textAlign(TextAlign.Center)
1430                .size({height: 100, width: "100%"})
1431            }.margin(10)
1432            .borderRadius(10)
1433            .backgroundColor("#FFFFFFFF")
1434          }, (item: string) => item)
1435          .onMove((from:number, to:number)=>{
1436            this.data.moveDataWithoutNotify(from, to)
1437          })
1438      }
1439      .width('100%')
1440      .height('100%')
1441      .backgroundColor("#FFDCDCDC")
1442    }
1443  }
1444  aboutToAppear(): void {
1445    for (let i = 0; i < 100; i++) {
1446      this.data.pushData(i.toString())
1447    }
1448  }
1449}
1450```
1451
1452**Figure 11** Drag and sort in LazyForEach 
1453![LazyForEach-Drag-Sort](figures/ForEach-Drag-Sort.gif)
1454
1455## FAQs
1456
1457- ### Unexpected Rendering Result
1458
1459  ```ts
1460  class BasicDataSource implements IDataSource {
1461    private listeners: DataChangeListener[] = [];
1462    private originDataArray: string[] = [];
1463  
1464    public totalCount(): number {
1465      return 0;
1466    }
1467  
1468    public getData(index: number): string {
1469      return this.originDataArray[index];
1470    }
1471  
1472    registerDataChangeListener(listener: DataChangeListener): void {
1473      if (this.listeners.indexOf(listener) < 0) {
1474        console.info('add listener');
1475        this.listeners.push(listener);
1476      }
1477    }
1478  
1479    unregisterDataChangeListener(listener: DataChangeListener): void {
1480      const pos = this.listeners.indexOf(listener);
1481      if (pos >= 0) {
1482        console.info('remove listener');
1483        this.listeners.splice(pos, 1);
1484      }
1485    }
1486  
1487    notifyDataReload(): void {
1488      this.listeners.forEach(listener => {
1489        listener.onDataReloaded();
1490      })
1491    }
1492  
1493    notifyDataAdd(index: number): void {
1494      this.listeners.forEach(listener => {
1495        listener.onDataAdd(index);
1496      })
1497    }
1498  
1499    notifyDataChange(index: number): void {
1500      this.listeners.forEach(listener => {
1501        listener.onDataChange(index);
1502      })
1503    }
1504  
1505    notifyDataDelete(index: number): void {
1506      this.listeners.forEach(listener => {
1507        listener.onDataDelete(index);
1508      })
1509    }
1510  
1511    notifyDataMove(from: number, to: number): void {
1512      this.listeners.forEach(listener => {
1513        listener.onDataMove(from, to);
1514      })
1515    }
1516  }
1517  
1518  class MyDataSource extends BasicDataSource {
1519    private dataArray: string[] = [];
1520  
1521    public totalCount(): number {
1522      return this.dataArray.length;
1523    }
1524  
1525    public getData(index: number): string {
1526      return this.dataArray[index];
1527    }
1528  
1529    public addData(index: number, data: string): void {
1530      this.dataArray.splice(index, 0, data);
1531      this.notifyDataAdd(index);
1532    }
1533  
1534    public pushData(data: string): void {
1535      this.dataArray.push(data);
1536      this.notifyDataAdd(this.dataArray.length - 1);
1537    }
1538    
1539    public deleteData(index: number): void {
1540      this.dataArray.splice(index, 1);
1541      this.notifyDataDelete(index);
1542    }
1543  }
1544  
1545  @Entry
1546  @Component
1547  struct MyComponent {
1548    private data: MyDataSource = new MyDataSource();
1549  
1550    aboutToAppear() {
1551      for (let i = 0; i <= 20; i++) {
1552        this.data.pushData(`Hello ${i}`)
1553      }
1554    }
1555  
1556    build() {
1557      List({ space: 3 }) {
1558        LazyForEach(this.data, (item: string, index: number) => {
1559          ListItem() {
1560            Row() {
1561              Text(item).fontSize(50)
1562                .onAppear(() => {
1563                  console.info("appear:" + item)
1564                })
1565            }.margin({ left: 10, right: 10 })
1566          }
1567          .onClick(() => {
1568            // Click to delete a child component.
1569            this.data.deleteData(index);
1570          })
1571        }, (item: string) => item)
1572      }.cachedCount(5)
1573    }
1574  }
1575  ```
1576
1577  **Figure 12** Unexpected data deletion by LazyForEach 
1578  ![LazyForEach-Render-Not-Expected](./figures/LazyForEach-Render-Not-Expected.gif)
1579
1580  When child components are clicked to be deleted, there may be cases where the deleted child component is not the one clicked. If this is the case, the indexes of data items are not updated correctly. In normal cases, after a child component is deleted, all data items following the data item of the child component should have their index decreased by 1. If these data items still use the original indexes, the indexes in **itemGenerator** do not change, resulting in the unexpected rendering result.
1581
1582  The following shows the code snippet after optimization:
1583
1584  ```ts
1585  class BasicDataSource implements IDataSource {
1586    private listeners: DataChangeListener[] = [];
1587    private originDataArray: string[] = [];
1588  
1589    public totalCount(): number {
1590      return 0;
1591    }
1592  
1593    public getData(index: number): string {
1594      return this.originDataArray[index];
1595    }
1596  
1597    registerDataChangeListener(listener: DataChangeListener): void {
1598      if (this.listeners.indexOf(listener) < 0) {
1599        console.info('add listener');
1600        this.listeners.push(listener);
1601      }
1602    }
1603  
1604    unregisterDataChangeListener(listener: DataChangeListener): void {
1605      const pos = this.listeners.indexOf(listener);
1606      if (pos >= 0) {
1607        console.info('remove listener');
1608        this.listeners.splice(pos, 1);
1609      }
1610    }
1611  
1612    notifyDataReload(): void {
1613      this.listeners.forEach(listener => {
1614        listener.onDataReloaded();
1615      })
1616    }
1617  
1618    notifyDataAdd(index: number): void {
1619      this.listeners.forEach(listener => {
1620        listener.onDataAdd(index);
1621      })
1622    }
1623  
1624    notifyDataChange(index: number): void {
1625      this.listeners.forEach(listener => {
1626        listener.onDataChange(index);
1627      })
1628    }
1629  
1630    notifyDataDelete(index: number): void {
1631      this.listeners.forEach(listener => {
1632        listener.onDataDelete(index);
1633      })
1634    }
1635  
1636    notifyDataMove(from: number, to: number): void {
1637      this.listeners.forEach(listener => {
1638        listener.onDataMove(from, to);
1639      })
1640    }
1641  }
1642  
1643  class MyDataSource extends BasicDataSource {
1644    private dataArray: string[] = [];
1645  
1646    public totalCount(): number {
1647      return this.dataArray.length;
1648    }
1649  
1650    public getData(index: number): string {
1651      return this.dataArray[index];
1652    }
1653  
1654    public addData(index: number, data: string): void {
1655      this.dataArray.splice(index, 0, data);
1656      this.notifyDataAdd(index);
1657    }
1658  
1659    public pushData(data: string): void {
1660      this.dataArray.push(data);
1661      this.notifyDataAdd(this.dataArray.length - 1);
1662    }
1663    
1664    public deleteData(index: number): void {
1665      this.dataArray.splice(index, 1);
1666      this.notifyDataDelete(index);
1667    }
1668      
1669    public reloadData(): void {
1670      this.notifyDataReload();
1671    }
1672  }
1673  
1674  @Entry
1675  @Component
1676  struct MyComponent {
1677    private data: MyDataSource = new MyDataSource();
1678  
1679    aboutToAppear() {
1680      for (let i = 0; i <= 20; i++) {
1681        this.data.pushData(`Hello ${i}`)
1682      }
1683    }
1684  
1685    build() {
1686      List({ space: 3 }) {
1687        LazyForEach(this.data, (item: string, index: number) => {
1688          ListItem() {
1689            Row() {
1690              Text(item).fontSize(50)
1691                .onAppear(() => {
1692                  console.info("appear:" + item)
1693                })
1694            }.margin({ left: 10, right: 10 })
1695          }
1696          .onClick(() => {
1697            // Click to delete a child component.
1698            this.data.deleteData(index);
1699            // Reset the indexes of all child components.
1700            this.data.reloadData();
1701          })
1702        }, (item: string, index: number) => item + index.toString())
1703      }.cachedCount(5)
1704    }
1705  }
1706  ```
1707
1708  After a data item is deleted, the **reloadData** method is called to rebuild the subsequent data items to update the indexes. To gurantee that **reload** method will rebuild data items, we must make sure that new keys be generated for the data items. Here `item + index.toString()` gurantee that subsequent data items of the deleted one will be rebuit. If we replace key generator by `item + Data.now().toString()`, new keys will be generated for all remaining data items, so they all will be rebuilt. This way, effect is the same, but the performance is slightly inferior.
1709
1710  **Figure 13** Fixing unexpected data deletion 
1711  ![LazyForEach-Render-Not-Expected-Repair](./figures/LazyForEach-Render-Not-Expected-Repair.gif)
1712
1713- ### Image Flickering During Re-renders
1714
1715  ```ts
1716  class BasicDataSource implements IDataSource {
1717    private listeners: DataChangeListener[] = [];
1718    private originDataArray: StringData[] = [];
1719  
1720    public totalCount(): number {
1721      return 0;
1722    }
1723  
1724    public getData(index: number): StringData {
1725      return this.originDataArray[index];
1726    }
1727  
1728    registerDataChangeListener(listener: DataChangeListener): void {
1729      if (this.listeners.indexOf(listener) < 0) {
1730        console.info('add listener');
1731        this.listeners.push(listener);
1732      }
1733    }
1734  
1735    unregisterDataChangeListener(listener: DataChangeListener): void {
1736      const pos = this.listeners.indexOf(listener);
1737      if (pos >= 0) {
1738        console.info('remove listener');
1739        this.listeners.splice(pos, 1);
1740      }
1741    }
1742  
1743    notifyDataReload(): void {
1744      this.listeners.forEach(listener => {
1745        listener.onDataReloaded();
1746      })
1747    }
1748  
1749    notifyDataAdd(index: number): void {
1750      this.listeners.forEach(listener => {
1751        listener.onDataAdd(index);
1752      })
1753    }
1754  
1755    notifyDataChange(index: number): void {
1756      this.listeners.forEach(listener => {
1757        listener.onDataChange(index);
1758      })
1759    }
1760  
1761    notifyDataDelete(index: number): void {
1762      this.listeners.forEach(listener => {
1763        listener.onDataDelete(index);
1764      })
1765    }
1766  
1767    notifyDataMove(from: number, to: number): void {
1768      this.listeners.forEach(listener => {
1769        listener.onDataMove(from, to);
1770      })
1771    }
1772  }
1773  
1774  class MyDataSource extends BasicDataSource {
1775    private dataArray: StringData[] = [];
1776  
1777    public totalCount(): number {
1778      return this.dataArray.length;
1779    }
1780  
1781    public getData(index: number): StringData {
1782      return this.dataArray[index];
1783    }
1784  
1785    public addData(index: number, data: StringData): void {
1786      this.dataArray.splice(index, 0, data);
1787      this.notifyDataAdd(index);
1788    }
1789  
1790    public pushData(data: StringData): void {
1791      this.dataArray.push(data);
1792      this.notifyDataAdd(this.dataArray.length - 1);
1793    }
1794      
1795    public reloadData(): void {
1796      this.notifyDataReload();
1797    }
1798  }
1799  
1800  class StringData {
1801    message: string;
1802    imgSrc: Resource;
1803    constructor(message: string, imgSrc: Resource) {
1804        this.message = message;
1805        this.imgSrc = imgSrc;
1806    }  
1807  }
1808  
1809  @Entry
1810  @Component
1811  struct MyComponent {
1812    private moved: number[] = [];
1813    private data: MyDataSource = new MyDataSource();
1814  
1815    aboutToAppear() {
1816      for (let i = 0; i <= 20; i++) {
1817        this.data.pushData(new StringData(`Hello ${i}`, $r('app.media.img')));
1818      }
1819    }
1820  
1821    build() {
1822      List({ space: 3 }) {
1823        LazyForEach(this.data, (item: StringData, index: number) => {
1824          ListItem() {
1825            Column() {
1826              Text(item.message).fontSize(50)
1827                .onAppear(() => {
1828                  console.info("appear:" + item.message)
1829                })
1830              Image(item.imgSrc)
1831                .width(500)
1832                .height(200)
1833            }.margin({ left: 10, right: 10 })
1834          }
1835          .onClick(() => {
1836            item.message += '00';
1837            this.data.reloadData();
1838          })
1839        }, (item: StringData, index: number) => JSON.stringify(item))
1840      }.cachedCount(5)
1841    }
1842  }
1843  ```
1844
1845  **Figure 14** Unwanted image flickering with LazyForEach 
1846  ![LazyForEach-Image-Flush](./figures/LazyForEach-Image-Flush.gif)
1847
1848  In the example, when a list item is clicked, only the **message** property of the item is changed. Yet, along with the text change comes the unwanted image flickering. This is because, with the **LazyForEach** update mechanism, the entire list item is rebuilt. As the **Image** component is updated asynchronously, flickering occurs. To address this issue, use @ObjectLink and @Observed so that only the **Text** component that uses the **item.message** property is re-rendered.
1849
1850  The following shows the code snippet after optimization:
1851
1852  ```ts
1853  class BasicDataSource implements IDataSource {
1854    private listeners: DataChangeListener[] = [];
1855    private originDataArray: StringData[] = [];
1856  
1857    public totalCount(): number {
1858      return 0;
1859    }
1860  
1861    public getData(index: number): StringData {
1862      return this.originDataArray[index];
1863    }
1864  
1865    registerDataChangeListener(listener: DataChangeListener): void {
1866      if (this.listeners.indexOf(listener) < 0) {
1867        console.info('add listener');
1868        this.listeners.push(listener);
1869      }
1870    }
1871  
1872    unregisterDataChangeListener(listener: DataChangeListener): void {
1873      const pos = this.listeners.indexOf(listener);
1874      if (pos >= 0) {
1875        console.info('remove listener');
1876        this.listeners.splice(pos, 1);
1877      }
1878    }
1879  
1880    notifyDataReload(): void {
1881      this.listeners.forEach(listener => {
1882        listener.onDataReloaded();
1883      })
1884    }
1885  
1886    notifyDataAdd(index: number): void {
1887      this.listeners.forEach(listener => {
1888        listener.onDataAdd(index);
1889      })
1890    }
1891  
1892    notifyDataChange(index: number): void {
1893      this.listeners.forEach(listener => {
1894        listener.onDataChange(index);
1895      })
1896    }
1897  
1898    notifyDataDelete(index: number): void {
1899      this.listeners.forEach(listener => {
1900        listener.onDataDelete(index);
1901      })
1902    }
1903  
1904    notifyDataMove(from: number, to: number): void {
1905      this.listeners.forEach(listener => {
1906        listener.onDataMove(from, to);
1907      })
1908    }
1909  }
1910  
1911  class MyDataSource extends BasicDataSource {
1912    private dataArray: StringData[] = [];
1913  
1914    public totalCount(): number {
1915      return this.dataArray.length;
1916    }
1917  
1918    public getData(index: number): StringData {
1919      return this.dataArray[index];
1920    }
1921  
1922    public addData(index: number, data: StringData): void {
1923      this.dataArray.splice(index, 0, data);
1924      this.notifyDataAdd(index);
1925    }
1926  
1927    public pushData(data: StringData): void {
1928      this.dataArray.push(data);
1929      this.notifyDataAdd(this.dataArray.length - 1);
1930    }
1931  }
1932  
1933  @Observed
1934  class StringData {
1935    message: string;
1936    imgSrc: Resource;
1937    constructor(message: string, imgSrc: Resource) {
1938        this.message = message;
1939        this.imgSrc = imgSrc;
1940    }  
1941  }
1942  
1943  @Entry
1944  @Component
1945  struct MyComponent {
1946    @State data: MyDataSource = new MyDataSource();
1947  
1948    aboutToAppear() {
1949      for (let i = 0; i <= 20; i++) {
1950        this.data.pushData(new StringData(`Hello ${i}`, $r('app.media.img')));
1951      }
1952    }
1953  
1954    build() {
1955      List({ space: 3 }) {
1956        LazyForEach(this.data, (item: StringData, index: number) => {
1957          ListItem() {
1958            ChildComponent({data: item})
1959          }
1960          .onClick(() => {
1961            item.message += '0';
1962          })
1963        }, (item: StringData, index: number) => index.toString())
1964      }.cachedCount(5)
1965    }
1966  }
1967  
1968  @Component
1969  struct ChildComponent {
1970    @ObjectLink data: StringData
1971    build() {
1972      Column() {
1973        Text(this.data.message).fontSize(50)
1974          .onAppear(() => {
1975            console.info("appear:" + this.data.message)
1976          })
1977        Image(this.data.imgSrc)
1978          .width(500)
1979          .height(200)
1980      }.margin({ left: 10, right: 10 })
1981    }
1982  }
1983  ```
1984
1985  **Figure 15** Fixing unwanted image flickering 
1986  ![LazyForEach-Image-Flush-Repair](./figures/LazyForEach-Image-Flush-Repair.gif)
1987
1988- ### UI Not Re-rendered When @ObjectLink Property Is Changed
1989
1990  ```ts
1991  class BasicDataSource implements IDataSource {
1992    private listeners: DataChangeListener[] = [];
1993    private originDataArray: StringData[] = [];
1994  
1995    public totalCount(): number {
1996      return 0;
1997    }
1998  
1999    public getData(index: number): StringData {
2000      return this.originDataArray[index];
2001    }
2002  
2003    registerDataChangeListener(listener: DataChangeListener): void {
2004      if (this.listeners.indexOf(listener) < 0) {
2005        console.info('add listener');
2006        this.listeners.push(listener);
2007      }
2008    }
2009  
2010    unregisterDataChangeListener(listener: DataChangeListener): void {
2011      const pos = this.listeners.indexOf(listener);
2012      if (pos >= 0) {
2013        console.info('remove listener');
2014        this.listeners.splice(pos, 1);
2015      }
2016    }
2017  
2018    notifyDataReload(): void {
2019      this.listeners.forEach(listener => {
2020        listener.onDataReloaded();
2021      })
2022    }
2023  
2024    notifyDataAdd(index: number): void {
2025      this.listeners.forEach(listener => {
2026        listener.onDataAdd(index);
2027      })
2028    }
2029  
2030    notifyDataChange(index: number): void {
2031      this.listeners.forEach(listener => {
2032        listener.onDataChange(index);
2033      })
2034    }
2035  
2036    notifyDataDelete(index: number): void {
2037      this.listeners.forEach(listener => {
2038        listener.onDataDelete(index);
2039      })
2040    }
2041  
2042    notifyDataMove(from: number, to: number): void {
2043      this.listeners.forEach(listener => {
2044        listener.onDataMove(from, to);
2045      })
2046    }
2047  }
2048  
2049  class MyDataSource extends BasicDataSource {
2050    private dataArray: StringData[] = [];
2051  
2052    public totalCount(): number {
2053      return this.dataArray.length;
2054    }
2055  
2056    public getData(index: number): StringData {
2057      return this.dataArray[index];
2058    }
2059  
2060    public addData(index: number, data: StringData): void {
2061      this.dataArray.splice(index, 0, data);
2062      this.notifyDataAdd(index);
2063    }
2064  
2065    public pushData(data: StringData): void {
2066      this.dataArray.push(data);
2067      this.notifyDataAdd(this.dataArray.length - 1);
2068    }
2069  }
2070  
2071  @Observed
2072  class StringData {
2073    message: NestedString;
2074    constructor(message: NestedString) {
2075      this.message = message;
2076    }  
2077  }
2078  
2079  @Observed
2080  class NestedString {
2081    message: string;
2082    constructor(message: string) {
2083      this.message = message;
2084    }  
2085  }
2086  
2087  @Entry
2088  @Component
2089  struct MyComponent {
2090    private moved: number[] = [];
2091    @State data: MyDataSource = new MyDataSource();
2092  
2093    aboutToAppear() {
2094      for (let i = 0; i <= 20; i++) {
2095        this.data.pushData(new StringData(new NestedString(`Hello ${i}`)));
2096      }
2097    }
2098  
2099    build() {
2100      List({ space: 3 }) {
2101        LazyForEach(this.data, (item: StringData, index: number) => {
2102          ListItem() {
2103            ChildComponent({data: item})
2104          }
2105          .onClick(() => {
2106            item.message.message += '0';
2107          })
2108        }, (item: StringData, index: number) => JSON.stringify(item) + index.toString())
2109      }.cachedCount(5)
2110    }
2111  }
2112  
2113  @Component
2114  struct ChildComponent {
2115    @ObjectLink data: StringData
2116    build() {
2117      Row() {
2118        Text(this.data.message.message).fontSize(50)
2119          .onAppear(() => {
2120            console.info("appear:" + this.data.message.message)
2121          })
2122      }.margin({ left: 10, right: 10 })
2123    }
2124  }
2125  ```
2126
2127  **Figure 16** UI not re-rendered when @ObjectLink property is changed 
2128  ![LazyForEach-ObjectLink-NotRenderUI](./figures/LazyForEach-ObjectLink-NotRenderUI.gif)
2129  
2130  The member variable decorated by @ObjectLink can observe only changes of its sub-properties, not changes of nested properties. Therefore, to instruct a component to re-render, we need to change the component sub-properties. For details, see [\@Observed and \@ObjectLink Decorators](./arkts-observed-and-objectlink.md).
2131  
2132  The following shows the code snippet after optimization:
2133  
2134  ```ts
2135  class BasicDataSource implements IDataSource {
2136    private listeners: DataChangeListener[] = [];
2137    private originDataArray: StringData[] = [];
2138  
2139    public totalCount(): number {
2140      return 0;
2141    }
2142  
2143    public getData(index: number): StringData {
2144      return this.originDataArray[index];
2145    }
2146  
2147    registerDataChangeListener(listener: DataChangeListener): void {
2148      if (this.listeners.indexOf(listener) < 0) {
2149        console.info('add listener');
2150        this.listeners.push(listener);
2151      }
2152    }
2153  
2154    unregisterDataChangeListener(listener: DataChangeListener): void {
2155      const pos = this.listeners.indexOf(listener);
2156      if (pos >= 0) {
2157        console.info('remove listener');
2158        this.listeners.splice(pos, 1);
2159      }
2160    }
2161  
2162    notifyDataReload(): void {
2163      this.listeners.forEach(listener => {
2164        listener.onDataReloaded();
2165      })
2166    }
2167  
2168    notifyDataAdd(index: number): void {
2169      this.listeners.forEach(listener => {
2170        listener.onDataAdd(index);
2171      })
2172    }
2173  
2174    notifyDataChange(index: number): void {
2175      this.listeners.forEach(listener => {
2176        listener.onDataChange(index);
2177      })
2178    }
2179  
2180    notifyDataDelete(index: number): void {
2181      this.listeners.forEach(listener => {
2182        listener.onDataDelete(index);
2183      })
2184    }
2185  
2186    notifyDataMove(from: number, to: number): void {
2187      this.listeners.forEach(listener => {
2188        listener.onDataMove(from, to);
2189      })
2190    }
2191  }
2192  
2193  class MyDataSource extends BasicDataSource {
2194    private dataArray: StringData[] = [];
2195  
2196    public totalCount(): number {
2197      return this.dataArray.length;
2198    }
2199  
2200    public getData(index: number): StringData {
2201      return this.dataArray[index];
2202    }
2203  
2204    public addData(index: number, data: StringData): void {
2205      this.dataArray.splice(index, 0, data);
2206      this.notifyDataAdd(index);
2207    }
2208  
2209    public pushData(data: StringData): void {
2210      this.dataArray.push(data);
2211      this.notifyDataAdd(this.dataArray.length - 1);
2212    }
2213  }
2214  
2215  @Observed
2216  class StringData {
2217    message: NestedString;
2218    constructor(message: NestedString) {
2219      this.message = message;
2220    }  
2221  }
2222  
2223  @Observed
2224  class NestedString {
2225    message: string;
2226    constructor(message: string) {
2227      this.message = message;
2228    }  
2229  }
2230  
2231  @Entry
2232  @Component
2233  struct MyComponent {
2234    private moved: number[] = [];
2235    @State data: MyDataSource = new MyDataSource();
2236  
2237    aboutToAppear() {
2238      for (let i = 0; i <= 20; i++) {
2239        this.data.pushData(new StringData(new NestedString(`Hello ${i}`)));
2240      }
2241    }
2242  
2243    build() {
2244      List({ space: 3 }) {
2245        LazyForEach(this.data, (item: StringData, index: number) => {
2246          ListItem() {
2247            ChildComponent({data: item})
2248          }
2249          .onClick(() => {
2250            item.message = new NestedString(item.message.message + '0');
2251          })
2252        }, (item: StringData, index: number) => JSON.stringify(item) + index.toString())
2253      }.cachedCount(5)
2254    }
2255  }
2256  
2257  @Component
2258  struct ChildComponent {
2259    @ObjectLink data: StringData
2260    build() {
2261      Row() {
2262        Text(this.data.message.message).fontSize(50)
2263          .onAppear(() => {
2264            console.info("appear:" + this.data.message.message)
2265          })
2266      }.margin({ left: 10, right: 10 })
2267    }
2268  }
2269  ```
2270  
2271  **Figure 17** Fixing the UI-not-re-rendered issue 
2272  ![LazyForEach-ObjectLink-NotRenderUI-Repair](./figures/LazyForEach-ObjectLink-NotRenderUI-Repair.gif)
2273
2274- ### Screen Flickering
2275List has an **onScrollIndex** callback function. When **onDataReloaded** is called in **onScrollIndex**, there is a risk of screen flickering.
2276
2277```ts
2278class BasicDataSource implements IDataSource {
2279  private listeners: DataChangeListener[] = [];
2280  private originDataArray: string[] = [];
2281
2282  public totalCount(): number {
2283    return 0;
2284  }
2285
2286  public getData(index: number): string {
2287    return this.originDataArray[index];
2288  }
2289
2290  registerDataChangeListener(listener: DataChangeListener): void {
2291    if (this.listeners.indexOf(listener) < 0) {
2292      console.info('add listener');
2293      this.listeners.push(listener);
2294    }
2295  }
2296
2297  unregisterDataChangeListener(listener: DataChangeListener): void {
2298    const pos = this.listeners.indexOf(listener);
2299    if (pos >= 0) {
2300      console.info('remove listener');
2301      this.listeners.splice(pos, 1);
2302    }
2303  }
2304
2305  notifyDataReload(): void {
2306    this.listeners.forEach(listener => {
2307      listener.onDataReloaded();
2308      // Method 2: listener.onDatasetChange([{type: DataOperationType.RELOAD}]);
2309    })
2310  }
2311
2312  notifyDataAdd(index: number): void {
2313    this.listeners.forEach(listener => {
2314      listener.onDataAdd(index);
2315    })
2316  }
2317
2318  notifyDataChange(index: number): void {
2319    this.listeners.forEach(listener => {
2320      listener.onDataChange(index);
2321    })
2322  }
2323
2324  notifyDataDelete(index: number): void {
2325    this.listeners.forEach(listener => {
2326      listener.onDataDelete(index);
2327    })
2328  }
2329
2330  notifyDataMove(from: number, to: number): void {
2331    this.listeners.forEach(listener => {
2332      listener.onDataMove(from, to);
2333    })
2334  }
2335
2336  notifyDatasetChange(operations: DataOperation[]):void{
2337    this.listeners.forEach(listener => {
2338      listener.onDatasetChange(operations);
2339    })
2340  }
2341}
2342
2343class MyDataSource extends BasicDataSource {
2344  private dataArray: string[] = [];
2345
2346  public totalCount(): number {
2347    return this.dataArray.length;
2348  }
2349
2350  public getData(index: number): string {
2351    return this.dataArray[index];
2352  }
2353
2354  public addData(index: number, data: string): void {
2355    this.dataArray.splice(index, 0, data);
2356    this.notifyDataAdd(index);
2357  }
2358
2359  public pushData(data: string): void {
2360    this.dataArray.push(data);
2361    this.notifyDataAdd(this.dataArray.length - 1);
2362  }
2363
2364  public deleteData(index: number): void {
2365    this.dataArray.splice(index, 1);
2366    this.notifyDataDelete(index);
2367  }
2368
2369  public changeData(index: number): void {
2370    this.notifyDataChange(index);
2371  }
2372
2373  operateData():void {
2374    const totalCount = this.dataArray.length;
2375    const batch=5;
2376    for (let i = totalCount; i < totalCount + batch; i++) {
2377      this.dataArray.push(`Hello ${i}`)
2378    }
2379    this.notifyDataReload();
2380  }
2381}
2382
2383@Entry
2384@Component
2385struct MyComponent {
2386  private moved: number[] = [];
2387  private data: MyDataSource = new MyDataSource();
2388
2389  aboutToAppear() {
2390    for (let i = 0; i <= 10; i++) {
2391      this.data.pushData(`Hello ${i}`)
2392    }
2393  }
2394
2395  build() {
2396    List({ space: 3 }) {
2397      LazyForEach(this.data, (item: string, index: number) => {
2398        ListItem() {
2399          Row() {
2400            Text(item)
2401              .width('100%')
2402              .height(80)
2403              .backgroundColor(Color.Gray)
2404              .onAppear(() => {
2405                console.info("appear:" + item)
2406              })
2407          }.margin({ left: 10, right: 10 })
2408        }
2409      }, (item: string) => item)
2410    }.cachedCount(10)
2411    .onScrollIndex((start, end, center) => {
2412      if (end === this.data.totalCount() - 1) {
2413        console.log('scroll to end')
2414        this.data.operateData();
2415      }
2416    })
2417  }
2418}
2419```
2420
2421When **List** is scrolled to the bottom, screen flicks like the following. 
2422![LazyForEach-Screen-Flicker](figures/LazyForEach-Screen-Flicker.gif)
2423
2424Replacing **onDataReloaded** by **onDatasetChange** cannot only fix this issue but also improves load performance.
2425
2426```ts
2427class BasicDataSource implements IDataSource {
2428  private listeners: DataChangeListener[] = [];
2429  private originDataArray: string[] = [];
2430
2431  public totalCount(): number {
2432    return 0;
2433  }
2434
2435  public getData(index: number): string {
2436    return this.originDataArray[index];
2437  }
2438
2439  registerDataChangeListener(listener: DataChangeListener): void {
2440    if (this.listeners.indexOf(listener) < 0) {
2441      console.info('add listener');
2442      this.listeners.push(listener);
2443    }
2444  }
2445
2446  unregisterDataChangeListener(listener: DataChangeListener): void {
2447    const pos = this.listeners.indexOf(listener);
2448    if (pos >= 0) {
2449      console.info('remove listener');
2450      this.listeners.splice(pos, 1);
2451    }
2452  }
2453
2454  notifyDataReload(): void {
2455    this.listeners.forEach(listener => {
2456      listener.onDataReloaded();
2457      // Method 2: listener.onDatasetChange([{type: DataOperationType.RELOAD}]);
2458    })
2459  }
2460
2461  notifyDataAdd(index: number): void {
2462    this.listeners.forEach(listener => {
2463      listener.onDataAdd(index);
2464    })
2465  }
2466
2467  notifyDataChange(index: number): void {
2468    this.listeners.forEach(listener => {
2469      listener.onDataChange(index);
2470    })
2471  }
2472
2473  notifyDataDelete(index: number): void {
2474    this.listeners.forEach(listener => {
2475      listener.onDataDelete(index);
2476    })
2477  }
2478
2479  notifyDataMove(from: number, to: number): void {
2480    this.listeners.forEach(listener => {
2481      listener.onDataMove(from, to);
2482    })
2483  }
2484
2485  notifyDatasetChange(operations: DataOperation[]):void{
2486    this.listeners.forEach(listener => {
2487      listener.onDatasetChange(operations);
2488    })
2489  }
2490}
2491
2492class MyDataSource extends BasicDataSource {
2493  private dataArray: string[] = [];
2494
2495  public totalCount(): number {
2496    return this.dataArray.length;
2497  }
2498
2499  public getData(index: number): string {
2500    return this.dataArray[index];
2501  }
2502
2503  public addData(index: number, data: string): void {
2504    this.dataArray.splice(index, 0, data);
2505    this.notifyDataAdd(index);
2506  }
2507
2508  public pushData(data: string): void {
2509    this.dataArray.push(data);
2510    this.notifyDataAdd(this.dataArray.length - 1);
2511  }
2512
2513  public deleteData(index: number): void {
2514    this.dataArray.splice(index, 1);
2515    this.notifyDataDelete(index);
2516  }
2517
2518  public changeData(index: number): void {
2519    this.notifyDataChange(index);
2520  }
2521
2522  operateData():void {
2523    const totalCount = this.dataArray.length;
2524    const batch=5;
2525    for (let i = totalCount; i < totalCount + batch; i++) {
2526      this.dataArray.push(`Hello ${i}`)
2527    }
2528    this.notifyDatasetChange([{type:DataOperationType.ADD, index: totalCount-1, count:batch}])
2529  }
2530}
2531
2532@Entry
2533@Component
2534struct MyComponent {
2535  private moved: number[] = [];
2536  private data: MyDataSource = new MyDataSource();
2537
2538  aboutToAppear() {
2539    for (let i = 0; i <= 10; i++) {
2540      this.data.pushData(`Hello ${i}`)
2541    }
2542  }
2543
2544  build() {
2545    List({ space: 3 }) {
2546      LazyForEach(this.data, (item: string, index: number) => {
2547        ListItem() {
2548          Row() {
2549            Text(item)
2550              .width('100%')
2551              .height(80)
2552              .backgroundColor(Color.Gray)
2553              .onAppear(() => {
2554                console.info("appear:" + item)
2555              })
2556          }.margin({ left: 10, right: 10 })
2557        }
2558      }, (item: string) => item)
2559    }.cachedCount(10)
2560    .onScrollIndex((start, end, center) => {
2561      if (end === this.data.totalCount() - 1) {
2562        console.log('scroll to end')
2563        this.data.operateData();
2564      }
2565    })
2566  }
2567}
2568```
2569
2570Fixed result 
2571![LazyForEach-Screen-Flicker-Repair](figures/LazyForEach-Screen-Flicker-Repair.gif)
2572