1e41f4b71Sopenharmony_ci# Repeat: Reusing Child Components
2e41f4b71Sopenharmony_ci
3e41f4b71Sopenharmony_ci>**NOTE**
4e41f4b71Sopenharmony_ci>
5e41f4b71Sopenharmony_ci>Repeat is supported since API version 12.
6e41f4b71Sopenharmony_ci>
7e41f4b71Sopenharmony_ci>State management V2 is still under development, and some features may be incomplete or not always work as expected.
8e41f4b71Sopenharmony_ci
9e41f4b71Sopenharmony_ciFor details about API parameters, see [Repeat APIs](https://gitee.com/openharmony/docs/blob/master/en/application-dev/reference/apis-arkui/arkui-ts/ts-rendering-control-repeat.md).
10e41f4b71Sopenharmony_ci
11e41f4b71Sopenharmony_ciWhen **virtualScroll** is disabled, the **Repeat** component, which is used together with the container component, performs loop rendering based on array data. In addition, the component returned by the API should be a child component that can be contained in the **Repeat** parent container. Compared with ForEach, **Repeat** optimizes the rendering performance in some update scenarios and generates function with the index maintained by the framework.
12e41f4b71Sopenharmony_ci
13e41f4b71Sopenharmony_ciWhen **virtualScroll** is enabled, **Repeat** iterates data from the provided data source as required and creates the corresponding component during each iteration. When **Repeat** is used in the scrolling container, the framework creates components as required based on the visible area of the scrolling container. When a component slides out of the visible area, the framework caches the component and uses it in the next iteration.
14e41f4b71Sopenharmony_ci
15e41f4b71Sopenharmony_ci> **Note:**
16e41f4b71Sopenharmony_ci>
17e41f4b71Sopenharmony_ci> The **virtualScroll** scenario of the **Repeat** component is not fully compatible with the decorators in V1. Using decorators in V1 together with **virtualScroll** scenario may cause rendering exceptions.
18e41f4b71Sopenharmony_ci
19e41f4b71Sopenharmony_ci## Constraints
20e41f4b71Sopenharmony_ci
21e41f4b71Sopenharmony_ci- **Repeat** must be used in container components. Only the following components support virtual scrolling: [List](../reference/apis-arkui/arkui-ts/ts-container-list.md), [ListItemGroup](../reference/apis-arkui/arkui-ts/ts-container-listitemgroup.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). In this case, **cachedCount** takes effect. Do not enable the **virtualScroll** function when other container components are using the **Repeat** component.
22e41f4b71Sopenharmony_ci- After **virtualScroll** is enabled for **Repeat**, only one child component can be created in each iteration. Otherwise, there is no constraint.
23e41f4b71Sopenharmony_ci- The generated child components must be allowed in the parent container component of **Repeat**.
24e41f4b71Sopenharmony_ci- **Repeat** can be included in an **if/else** statement, and can also contain such a statement.
25e41f4b71Sopenharmony_ci- **Repeat** uses key as identifiers internally. Therefore, the key generator must generate a unique value for each piece of data. If the key generated for multiple pieces of data at the same time are the same, UI component rendering will be faulty.
26e41f4b71Sopenharmony_ci- If **virtualScroll** is disabled, the **template** is not supported currently and problems may occur when reusing.
27e41f4b71Sopenharmony_ci- When **Repeat** and **\@Builder** are used together, parameters of the **RepeatItem** type must be passed so that the component can listen for data changes. If only **RepeatItem.item** or **RepeatItem.index** is passed, UI rendering exceptions occur.
28e41f4b71Sopenharmony_ci- In the virtualScroll scenario, the value of totalCount is customized. When the length of data source changes, the value of totalCount should be manually updated. Otherwise, the rendering exception occurs on the list display area.
29e41f4b71Sopenharmony_ci
30e41f4b71Sopenharmony_ci## Key Generation Rules
31e41f4b71Sopenharmony_ci
32e41f4b71Sopenharmony_ci### non-virtualScroll
33e41f4b71Sopenharmony_ci
34e41f4b71Sopenharmony_ci![Repeat-Slide](./figures/Repeat-NonVirtualScroll-Key.png)
35e41f4b71Sopenharmony_ci
36e41f4b71Sopenharmony_ci### virtualScroll
37e41f4b71Sopenharmony_ci
38e41f4b71Sopenharmony_ci**virtualScroll** has a key generation rule similar to that of **non-virtualScroll.** However, it does not automatically handle the duplicate keys, so you need to ensure that the keys are unique.
39e41f4b71Sopenharmony_ci
40e41f4b71Sopenharmony_ci![Repeat-Slide](./figures/Repeat-VirtualScroll-Key.png)
41e41f4b71Sopenharmony_ci
42e41f4b71Sopenharmony_ci## Component Generation and Reuse Rules
43e41f4b71Sopenharmony_ci
44e41f4b71Sopenharmony_ci### non-virtualScroll
45e41f4b71Sopenharmony_ci
46e41f4b71Sopenharmony_ciAll child components are created when **Repeat** is rendered for the first time. The original components are reused when data is updated.
47e41f4b71Sopenharmony_ci
48e41f4b71Sopenharmony_ciWhen the **Repeat** component updates data, it compares all keys in the last update with those in the latest update. If the current key is the same as the last one, **Repeat** reuses the child component and updates the **RepeatItem.index** index.
49e41f4b71Sopenharmony_ci
50e41f4b71Sopenharmony_ciAfter **Repeat** compares all duplicate keys and reuses them, if the last key is unique and a new key is generated after this update, a child component needs to be created. In this case, **Repeat** will reuse redundant child components, update the **RepeatItem.item** data source and **RepeatItem.index** index, and re-render the UI.
51e41f4b71Sopenharmony_ci
52e41f4b71Sopenharmony_ciIf the number of remaining child components is greater than or equal to the number of newly updated components, the components are fully reused and redundant components are released. If the number of remaining child components is less than the number of newly updated components, **Repeat** will create components corresponding to the extra data items after the remaining data items are all reused.
53e41f4b71Sopenharmony_ci
54e41f4b71Sopenharmony_ci### virtualScroll
55e41f4b71Sopenharmony_ci
56e41f4b71Sopenharmony_ciAt the first time when **Repeat** renders child components, only the required component is generated. During sliding and data update, nodes on the lower screen are cached. When a new component needs to be generated, the cached component is reused.
57e41f4b71Sopenharmony_ci
58e41f4b71Sopenharmony_ci#### Slide shortcut
59e41f4b71Sopenharmony_ci
60e41f4b71Sopenharmony_ciThe following figure describes the node state before sliding.
61e41f4b71Sopenharmony_ci
62e41f4b71Sopenharmony_ci![Repeat-Start](./figures/Repeat-Start.png)
63e41f4b71Sopenharmony_ci
64e41f4b71Sopenharmony_ciCurrently, the **Repeat** component has two types of templateId. **templateId a** sets three as its maximum cache value for the corresponding cache pool. **templateId b** sets four as its maximum cache value and preloads one note for its parent components by default. Now swipe right on the screen, and **Repeat** will reuse the nodes in the cache pool.
65e41f4b71Sopenharmony_ci
66e41f4b71Sopenharmony_ci![Repeat-Slide](./figures/Repeat-Slide.png)
67e41f4b71Sopenharmony_ci
68e41f4b71Sopenharmony_ciThe data of **index=18** enters the screen and the preloading range of the parent component, coming up with a result of **templateId b**. In this case, **Repeat** obtains a node from the **type=b** cache pool for reuse and updates its key, index, and data. Other grandchildren notes that use the data and index in the child node are updated based on the state management V2 rules.
69e41f4b71Sopenharmony_ci
70e41f4b71Sopenharmony_ciThe **index=10** note slides out of the screen and the preloading range of the parent component. When the UI main thread is idle, it checks whether the **type=a** cache pool has sufficient space. In this case, there are four nodes in the cache pool, which exceeds the rated three, so **Repeat** will release the last node.
71e41f4b71Sopenharmony_ci
72e41f4b71Sopenharmony_ci![Repeat-Slide-Done](./figures/Repeat-Slide-Done.png)
73e41f4b71Sopenharmony_ci
74e41f4b71Sopenharmony_ci#### Data Update Scenarios
75e41f4b71Sopenharmony_ci
76e41f4b71Sopenharmony_ci![Repeat-Start](./figures/Repeat-Start.png)
77e41f4b71Sopenharmony_ci
78e41f4b71Sopenharmony_ciIn this case, delete the **index=12** node, update the data of the **index=13** node, change the **templateId b** to **templateId a** of the **index=14** node, and update the key of the **index=15** node.
79e41f4b71Sopenharmony_ci
80e41f4b71Sopenharmony_ci![Repeat-Update1](./figures/Repeat-Update1.png)
81e41f4b71Sopenharmony_ci
82e41f4b71Sopenharmony_ciNow, **Repeat** notifies the parent component to re-lay out the nodes and compares the keys one by one. If the template ID of the node is the same as that of the original one, the note is reused to update the **key**, **index** and **data**. Otherwise, the node in the cache pool with the same template ID is reused to update the **key**, **index**, and **data**.
83e41f4b71Sopenharmony_ci
84e41f4b71Sopenharmony_ci![Repeat-Update2](./figures/Repeat-Update2.png)
85e41f4b71Sopenharmony_ci
86e41f4b71Sopenharmony_ciAs shown in the preceding figure, node13 updates **data** and **index**; node14 updates the template ID and **index** and reuses a node from the cache pool; node15 reuses its own node and updates the **key**, **index**, and **data** synchronously because of the changed **key** and the unchanged template ID; node 16 and node 17 only update the **index**. The **index=17** node is new and reused from the cache pool.
87e41f4b71Sopenharmony_ci
88e41f4b71Sopenharmony_ci![Repeat-Update-Done](./figures/Repeat-Update-Done.png)
89e41f4b71Sopenharmony_ci
90e41f4b71Sopenharmony_ci## cachedCount Rules
91e41f4b71Sopenharmony_ci
92e41f4b71Sopenharmony_ciThe differences between the **.cachedCount** attribute of the the **List** or **Grid** component and the **cachedCount** attribute of the **Repeat** must be clarified. Both are used to balance performance and memory, but their definitions are different.
93e41f4b71Sopenharmony_ci- **.cachedCount** of **List** or **Grid**: indicates the nodes that are located in the component tree and treated as invisible. Container components such as **List** or **Grid** render these nodes to achieve better performance. But **Repeat** treats these nodes as visible.
94e41f4b71Sopenharmony_ci- template `cachedCount`: indicates the nodes that are treated as invisible by **Repeat**. These nodes are idle and are temporarily stored in the framework. You can update these nodes as required to implement reuse.
95e41f4b71Sopenharmony_ci
96e41f4b71Sopenharmony_ci## Use Scenarios
97e41f4b71Sopenharmony_ci
98e41f4b71Sopenharmony_ci### non-virtualScroll
99e41f4b71Sopenharmony_ci
100e41f4b71Sopenharmony_ci#### Changing the Data Source
101e41f4b71Sopenharmony_ci
102e41f4b71Sopenharmony_ciWhen **Repeat** component implements the non-initial rendering, it compares all keys in the last update with those in the latest update. If the current key is the same as the last one, **Repeat** reuses the child component and updates the **RepeatItem.index** index.
103e41f4b71Sopenharmony_ci
104e41f4b71Sopenharmony_ciAfter **Repeat** compares all duplicate keys and reuses them, if the last key is unique and a new key is generated after this update, a child component needs to be created. In this case, **Repeat** will reuse redundant child components and update the **RepeatItem.item** data source and **RepeatItem.index** index.
105e41f4b71Sopenharmony_ci
106e41f4b71Sopenharmony_ciIf the number of remaining child components is greater than or equal to the number of newly updated components, the components are fully reused. If the number of remaining child components is less than the number of newly updated components, **Repeat** will create components corresponding to the extra data items after the remaining components are all reused.
107e41f4b71Sopenharmony_ci
108e41f4b71Sopenharmony_ci```ts
109e41f4b71Sopenharmony_ci@Entry
110e41f4b71Sopenharmony_ci@ComponentV2
111e41f4b71Sopenharmony_cistruct Parent {
112e41f4b71Sopenharmony_ci  @Local simpleList: Array<string> = ['one', 'two', 'three'];
113e41f4b71Sopenharmony_ci
114e41f4b71Sopenharmony_ci  build() {
115e41f4b71Sopenharmony_ci    Row() {
116e41f4b71Sopenharmony_ci      Column() {
117e41f4b71Sopenharmony_ci        Text('Click to change the value of the third array item')
118e41f4b71Sopenharmony_ci          .fontSize(24)
119e41f4b71Sopenharmony_ci          .fontColor(Color.Red)
120e41f4b71Sopenharmony_ci          .onClick(() => {
121e41f4b71Sopenharmony_ci            this.simpleList[2] = 'new three';
122e41f4b71Sopenharmony_ci          })
123e41f4b71Sopenharmony_ci
124e41f4b71Sopenharmony_ci        Repeat<string>(this.simpleList)
125e41f4b71Sopenharmony_ci            .each((obj: RepeatItem<string>)=>{
126e41f4b71Sopenharmony_ci              ChildItem({ item: obj.item })
127e41f4b71Sopenharmony_ci                .margin({top: 20})
128e41f4b71Sopenharmony_ci            })
129e41f4b71Sopenharmony_ci            .key((item: string) => item)
130e41f4b71Sopenharmony_ci      }
131e41f4b71Sopenharmony_ci      .justifyContent(FlexAlign.Center)
132e41f4b71Sopenharmony_ci      .width('100%')
133e41f4b71Sopenharmony_ci      .height('100%')
134e41f4b71Sopenharmony_ci    }
135e41f4b71Sopenharmony_ci    .height('100%')
136e41f4b71Sopenharmony_ci    .backgroundColor(0xF1F3F5)
137e41f4b71Sopenharmony_ci  }
138e41f4b71Sopenharmony_ci}
139e41f4b71Sopenharmony_ci
140e41f4b71Sopenharmony_ci@ComponentV2
141e41f4b71Sopenharmony_cistruct ChildItem {
142e41f4b71Sopenharmony_ci  @Param @Require item: string;
143e41f4b71Sopenharmony_ci
144e41f4b71Sopenharmony_ci  build() {
145e41f4b71Sopenharmony_ci    Text(this.item)
146e41f4b71Sopenharmony_ci      .fontSize(30)
147e41f4b71Sopenharmony_ci  }
148e41f4b71Sopenharmony_ci}
149e41f4b71Sopenharmony_ci```
150e41f4b71Sopenharmony_ci
151e41f4b71Sopenharmony_ci![ForEach-Non-Initial-Render-Case-Effect](./figures/ForEach-Non-Initial-Render-Case-Effect.gif)
152e41f4b71Sopenharmony_ci
153e41f4b71Sopenharmony_ciThe component of the third array item is reused when the array item is re-rendered, and only the data is refreshed.
154e41f4b71Sopenharmony_ci
155e41f4b71Sopenharmony_ci#### Changing the Index Value
156e41f4b71Sopenharmony_ci
157e41f4b71Sopenharmony_ciIn the following example, when array items 1 and 2 are exchanged, if the key is as the same as the last one, **Repeat** reuses the previous component and updates only the data of the component that uses the **index** value.
158e41f4b71Sopenharmony_ci
159e41f4b71Sopenharmony_ci```ts
160e41f4b71Sopenharmony_ci@Entry
161e41f4b71Sopenharmony_ci@ComponentV2
162e41f4b71Sopenharmony_cistruct Parent {
163e41f4b71Sopenharmony_ci  @Local simpleList: Array<string> = ['one', 'two', 'three'];
164e41f4b71Sopenharmony_ci
165e41f4b71Sopenharmony_ci  build() {
166e41f4b71Sopenharmony_ci    Row() {
167e41f4b71Sopenharmony_ci      Column() {
168e41f4b71Sopenharmony_ci        Text ('Exchange array items 1 and 2')
169e41f4b71Sopenharmony_ci          .fontSize(24)
170e41f4b71Sopenharmony_ci          .fontColor(Color.Red)
171e41f4b71Sopenharmony_ci          .onClick(() => {
172e41f4b71Sopenharmony_ci            let temp: string = this.simpleList[2]
173e41f4b71Sopenharmony_ci            this.simpleList[2] = this.simpleList[1]
174e41f4b71Sopenharmony_ci            this.simpleList[1] = temp
175e41f4b71Sopenharmony_ci          })
176e41f4b71Sopenharmony_ci          .margin({bottom: 20})
177e41f4b71Sopenharmony_ci
178e41f4b71Sopenharmony_ci        Repeat<string>(this.simpleList)
179e41f4b71Sopenharmony_ci          .each((obj: RepeatItem<string>)=>{
180e41f4b71Sopenharmony_ci            Text("index: " + obj.index)
181e41f4b71Sopenharmony_ci              .fontSize(30)
182e41f4b71Sopenharmony_ci            ChildItem({ item: obj.item })
183e41f4b71Sopenharmony_ci              .margin({bottom: 20})
184e41f4b71Sopenharmony_ci          })
185e41f4b71Sopenharmony_ci          .key((item: string) => item)
186e41f4b71Sopenharmony_ci      }
187e41f4b71Sopenharmony_ci      .justifyContent(FlexAlign.Center)
188e41f4b71Sopenharmony_ci      .width('100%')
189e41f4b71Sopenharmony_ci      .height('100%')
190e41f4b71Sopenharmony_ci    }
191e41f4b71Sopenharmony_ci    .height('100%')
192e41f4b71Sopenharmony_ci    .backgroundColor(0xF1F3F5)
193e41f4b71Sopenharmony_ci  }
194e41f4b71Sopenharmony_ci}
195e41f4b71Sopenharmony_ci
196e41f4b71Sopenharmony_ci@ComponentV2
197e41f4b71Sopenharmony_cistruct ChildItem {
198e41f4b71Sopenharmony_ci  @Param @Require item: string;
199e41f4b71Sopenharmony_ci
200e41f4b71Sopenharmony_ci  build() {
201e41f4b71Sopenharmony_ci    Text(this.item)
202e41f4b71Sopenharmony_ci      .fontSize(30)
203e41f4b71Sopenharmony_ci  }
204e41f4b71Sopenharmony_ci}
205e41f4b71Sopenharmony_ci```
206e41f4b71Sopenharmony_ci
207e41f4b71Sopenharmony_ci![Repeat-Non-Initial-Render-Case-Exchange-Effect](./figures/Repeat-Non-Initial-Render-Case-Exchange-Effect.gif)
208e41f4b71Sopenharmony_ci
209e41f4b71Sopenharmony_ci### virtualScroll
210e41f4b71Sopenharmony_ci
211e41f4b71Sopenharmony_ciThis section describes the actual application scenarios of **Repeat** and the reuse of component nodes in the **virtualScroll** scenario. A large number of test scenarios can be derived based on reuse rules. This section only describes typical data changes.
212e41f4b71Sopenharmony_ci
213e41f4b71Sopenharmony_ci#### Examples
214e41f4b71Sopenharmony_ci
215e41f4b71Sopenharmony_ciThe following code designs typical data source operations in the **virtualScroll** scenario of the **Repeat** component, including **inserting, modifying, deleting, and exchanging data**. Click the corresponding text to trigger the data change. Click two data items in sequence to exchange them.
216e41f4b71Sopenharmony_ci
217e41f4b71Sopenharmony_ci```ts
218e41f4b71Sopenharmony_ci@ObservedV2
219e41f4b71Sopenharmony_ciclass Clazz {
220e41f4b71Sopenharmony_ci  @Trace message: string = '';
221e41f4b71Sopenharmony_ci
222e41f4b71Sopenharmony_ci  constructor(message: string) {
223e41f4b71Sopenharmony_ci    this.message = message;
224e41f4b71Sopenharmony_ci  }
225e41f4b71Sopenharmony_ci}
226e41f4b71Sopenharmony_ci
227e41f4b71Sopenharmony_ci@Entry
228e41f4b71Sopenharmony_ci@ComponentV2
229e41f4b71Sopenharmony_cistruct TestPage {
230e41f4b71Sopenharmony_ci  @Local simpleList: Array<Clazz> = [];
231e41f4b71Sopenharmony_ci  private exchange: number[] = [];
232e41f4b71Sopenharmony_ci  private counter: number = 0;
233e41f4b71Sopenharmony_ci
234e41f4b71Sopenharmony_ci  aboutToAppear(): void {
235e41f4b71Sopenharmony_ci    for (let i = 0; i < 100; i++) {
236e41f4b71Sopenharmony_ci      this.simpleList.push(new Clazz('Hello ' + i));
237e41f4b71Sopenharmony_ci    }
238e41f4b71Sopenharmony_ci  }
239e41f4b71Sopenharmony_ci
240e41f4b71Sopenharmony_ci  build() {
241e41f4b71Sopenharmony_ci    Column({ space: 10 }) {
242e41f4b71Sopenharmony_ci      Text('Click to insert the fifth item.')
243e41f4b71Sopenharmony_ci        .fontSize(24)
244e41f4b71Sopenharmony_ci        .fontColor(Color.Red)
245e41f4b71Sopenharmony_ci        .onClick(() => {
246e41f4b71Sopenharmony_ci          this.simpleList.splice(4, 0, new Clazz(`${this.counter++}_new item`));
247e41f4b71Sopenharmony_ci        })
248e41f4b71Sopenharmony_ci      Text('Click to modify the fifth item.')
249e41f4b71Sopenharmony_ci        .fontSize(24)
250e41f4b71Sopenharmony_ci        .fontColor(Color.Red)
251e41f4b71Sopenharmony_ci        .onClick(() => {
252e41f4b71Sopenharmony_ci          this.simpleList[4].message = `${this.counter++}_new item`;
253e41f4b71Sopenharmony_ci        })
254e41f4b71Sopenharmony_ci      Text ('Click to delete the fifth item.')
255e41f4b71Sopenharmony_ci        .fontSize(24)
256e41f4b71Sopenharmony_ci        .fontColor(Color.Red)
257e41f4b71Sopenharmony_ci        .onClick(() => {
258e41f4b71Sopenharmony_ci          this.simpleList.splice(4, 1);
259e41f4b71Sopenharmony_ci        })
260e41f4b71Sopenharmony_ci      Text('Click two items to change them.')
261e41f4b71Sopenharmony_ci        .fontSize(24)
262e41f4b71Sopenharmony_ci        .fontColor(Color.Red)
263e41f4b71Sopenharmony_ci
264e41f4b71Sopenharmony_ci      List({ initialIndex: 10 }) {
265e41f4b71Sopenharmony_ci        Repeat<Clazz>(this.simpleList)
266e41f4b71Sopenharmony_ci          .each((obj: RepeatItem<Clazz>) => {
267e41f4b71Sopenharmony_ci            ListItem() {
268e41f4b71Sopenharmony_ci              Text('[each] ' + obj.item.message)
269e41f4b71Sopenharmony_ci                .fontSize(30)
270e41f4b71Sopenharmony_ci                .margin({ top: 10 })
271e41f4b71Sopenharmony_ci            }
272e41f4b71Sopenharmony_ci          })
273e41f4b71Sopenharmony_ci          .key((item: Clazz, index: number) => {
274e41f4b71Sopenharmony_ci            return item.message;
275e41f4b71Sopenharmony_ci          })
276e41f4b71Sopenharmony_ci          .virtualScroll({ totalCount: this.simpleList.length })
277e41f4b71Sopenharmony_ci          .templateId((item: Clazz, index: number) => "default")
278e41f4b71Sopenharmony_ci          .template('default', (ri) => {
279e41f4b71Sopenharmony_ci            Text('[template] ' + ri.item.message)
280e41f4b71Sopenharmony_ci              .fontSize(30)
281e41f4b71Sopenharmony_ci              .margin({ top: 10 })
282e41f4b71Sopenharmony_ci              .onClick(() => {
283e41f4b71Sopenharmony_ci                this.exchange.push(ri.index);
284e41f4b71Sopenharmony_ci                if (this.exchange.length === 2) {
285e41f4b71Sopenharmony_ci                  let _a = this.exchange[0];
286e41f4b71Sopenharmony_ci                  let _b = this.exchange[1];
287e41f4b71Sopenharmony_ci                  // click to exchange
288e41f4b71Sopenharmony_ci                  let temp: string = this.simpleList[_a].message;
289e41f4b71Sopenharmony_ci                  this.simpleList[_a].message = this.simpleList[_b].message;
290e41f4b71Sopenharmony_ci                  this.simpleList[_b].message = temp;
291e41f4b71Sopenharmony_ci                  this.exchange = [];
292e41f4b71Sopenharmony_ci                }
293e41f4b71Sopenharmony_ci              })
294e41f4b71Sopenharmony_ci          }, { cachedCount: 3 })
295e41f4b71Sopenharmony_ci      }
296e41f4b71Sopenharmony_ci      .cachedCount(1)
297e41f4b71Sopenharmony_ci      .border({ width: 1 })
298e41f4b71Sopenharmony_ci      .width('90%')
299e41f4b71Sopenharmony_ci      .height('70%')
300e41f4b71Sopenharmony_ci    }
301e41f4b71Sopenharmony_ci    .height('100%')
302e41f4b71Sopenharmony_ci    .justifyContent(FlexAlign.Center)
303e41f4b71Sopenharmony_ci  }
304e41f4b71Sopenharmony_ci}
305e41f4b71Sopenharmony_ci```
306e41f4b71Sopenharmony_ciThe following figure lists 100 **message** string properties of the custom class **Clazz**. The **cachedCount** of the **List** component is set to 1, and the size of the **template "default"** cache pool is set to 3. The application screen is shown as bellow.
307e41f4b71Sopenharmony_ci
308e41f4b71Sopenharmony_ci![Repeat-VirtualScroll-Demo](./figures/Repeat-VirtualScroll-Demo.jpg)
309e41f4b71Sopenharmony_ci
310e41f4b71Sopenharmony_ci#### Node Operation Instance
311e41f4b71Sopenharmony_ci
312e41f4b71Sopenharmony_ciWhen the data source is changed, the node whose key is changed will be re-created. If a cache node exists in the cache pool of the corresponding template, the node is reused. When the **key** remains unchanged, the component reuses and updates the **index** value.
313e41f4b71Sopenharmony_ci
314e41f4b71Sopenharmony_ci**Inserting Data**
315e41f4b71Sopenharmony_ci
316e41f4b71Sopenharmony_ciOperations
317e41f4b71Sopenharmony_ci
318e41f4b71Sopenharmony_ci![Repeat-VirtualScroll-InsertData](./figures/Repeat-VirtualScroll-InsertData.gif)
319e41f4b71Sopenharmony_ci
320e41f4b71Sopenharmony_ciThis example shows four data insertions. Two data items are inserted on the upper part of the screen for the first two times, and another two are inserted on the current screen for the last two times. Print the execution state of the **onUpdateNode** function. "[Old key]->[New key]" indicates that the old node reuses the new node. The node reuse is as follows:
321e41f4b71Sopenharmony_ci
322e41f4b71Sopenharmony_ci```
323e41f4b71Sopenharmony_ci// Insert data twice on the upper part of the screen.
324e41f4b71Sopenharmony_cionUpdateNode [Hello 22] -> [Hello 8]
325e41f4b71Sopenharmony_cionUpdateNode [Hello 21] -> [Hello 7]
326e41f4b71Sopenharmony_ci// Insert data twice on the current screen.
327e41f4b71Sopenharmony_cionUpdateNode [Hello 11] -> [2_new item]
328e41f4b71Sopenharmony_cionUpdateNode [Hello 10] -> [3_new item]
329e41f4b71Sopenharmony_ci```
330e41f4b71Sopenharmony_ci
331e41f4b71Sopenharmony_ciWhen data is inserted on the upper part of the screen, the nodes move. As a result, the pre-loading node of the current screen changes and is reused. That is, the node 22 that exits the cache in the lower part is reused by the node 8 that enters the cache in the upper part. When data is inserted into the current screen, a new data item is generated. The new node will reuse the cached pre-loading node on the lower part of the screen. Data will not be reused when you add data to the lower part of the screen.
332e41f4b71Sopenharmony_ci
333e41f4b71Sopenharmony_ci**Modifying Data**
334e41f4b71Sopenharmony_ci
335e41f4b71Sopenharmony_ciOperations
336e41f4b71Sopenharmony_ci
337e41f4b71Sopenharmony_ci![Repeat-VirtualScroll-ModifyData](./figures/Repeat-VirtualScroll-ModifyData.gif)
338e41f4b71Sopenharmony_ci
339e41f4b71Sopenharmony_ciThis example shows four data modifications. Two data items are modified on the upper part of the screen for the first two times, and another two are modified on the current screen for the last two times. Print the execution state of the **onUpdateNode** function. "[Old key]->[New key]" indicates that the old node reuses the new node. The node reuse is as follows:
340e41f4b71Sopenharmony_ci
341e41f4b71Sopenharmony_ci```
342e41f4b71Sopenharmony_ci// Modify data twice on the current screen.
343e41f4b71Sopenharmony_cionUpdateNode [1_new item] -> [2_new item]
344e41f4b71Sopenharmony_cionUpdateNode [2_new item] -> [3_new item]
345e41f4b71Sopenharmony_ci```
346e41f4b71Sopenharmony_ci
347e41f4b71Sopenharmony_ciBecause the rendering nodes does not exist in the upper or lower part of the screen, node reuse does not occur. When a node on the current screen is modified, the template ID of the node does not change. Therefore, the node is reused.
348e41f4b71Sopenharmony_ci
349e41f4b71Sopenharmony_ci**Exchanging Data**
350e41f4b71Sopenharmony_ci
351e41f4b71Sopenharmony_ciOperations
352e41f4b71Sopenharmony_ci
353e41f4b71Sopenharmony_ci![Repeat-VirtualScroll-ExchangeData](./figures/Repeat-VirtualScroll-ExchangeData.gif)
354e41f4b71Sopenharmony_ci
355e41f4b71Sopenharmony_ciThis example shows two data exchanges. Exchanging two nodes does not change the keys, so no node will be reused.
356e41f4b71Sopenharmony_ci
357e41f4b71Sopenharmony_ci**Deleting Data**
358e41f4b71Sopenharmony_ci
359e41f4b71Sopenharmony_ciOperations
360e41f4b71Sopenharmony_ci
361e41f4b71Sopenharmony_ci![Repeat-VirtualScroll-DeleteData](./figures/Repeat-VirtualScroll-DeleteData.gif)
362e41f4b71Sopenharmony_ci
363e41f4b71Sopenharmony_ciThis example shows five data deletions. Two data items are deleted on the upper part of the screen for the first two times, and another three are deleted on the current screen for the last three times. Print the execution state of the **onUpdateNode** function. "[Old key]->[New key]" indicates that the old node reuses the new node. The node reuse is as follows:
364e41f4b71Sopenharmony_ci
365e41f4b71Sopenharmony_ci```
366e41f4b71Sopenharmony_ci// Delete data twice on the upper part of the screen.
367e41f4b71Sopenharmony_cionUpdateNode [Hello 9] -> [Hello 23]
368e41f4b71Sopenharmony_cionUpdateNode [Hello 10] -> [Hello 24]
369e41f4b71Sopenharmony_ci// The onUpdateNode function is not called when the data is deleted twice on the current screen.
370e41f4b71Sopenharmony_ci// The data on the current screen is deleted for the third time.
371e41f4b71Sopenharmony_cionUpdateNode [Hello 6] -> [Hello 17]
372e41f4b71Sopenharmony_ci```
373e41f4b71Sopenharmony_ci
374e41f4b71Sopenharmony_ciWhen data is deleted from the upper part of the screen, the nodes move. As a result, the pre-loading node of the current screen changes and is reused. That is, the node 9 that exits the cache in the upper part is reused by the node 23 that enters the cache in the lower part. When data is deleted from the current screen, because of the **cachedCount** pre-loading property of the **List** component, the node that enters the screen in the first two deletions has been rendered and will not be reused. The deleted node enters the cache pool of the corresponding template. In the third deletion, the pre-loading node 17 that enters from the lower part reuses the node 6 in the cache pool.
375e41f4b71Sopenharmony_ci
376e41f4b71Sopenharmony_ci#### Using Multiple Templates
377e41f4b71Sopenharmony_ci
378e41f4b71Sopenharmony_ci```
379e41f4b71Sopenharmony_ci@ObservedV2
380e41f4b71Sopenharmony_ciclass Wrap1 {
381e41f4b71Sopenharmony_ci    @Trace message: string = '';
382e41f4b71Sopenharmony_ci    
383e41f4b71Sopenharmony_ci    constructor(message: string) {
384e41f4b71Sopenharmony_ci        this.message = message;
385e41f4b71Sopenharmony_ci    }
386e41f4b71Sopenharmony_ci}
387e41f4b71Sopenharmony_ci
388e41f4b71Sopenharmony_ci@Entry
389e41f4b71Sopenharmony_ci@ComponentV2
390e41f4b71Sopenharmony_cistruct Parent {
391e41f4b71Sopenharmony_ci    @Local simpleList: Array<Wrap1> = [];
392e41f4b71Sopenharmony_ci    
393e41f4b71Sopenharmony_ci    aboutToAppear(): void {
394e41f4b71Sopenharmony_ci        for (let i=0; i<100; i++) {
395e41f4b71Sopenharmony_ci            this.simpleList.push(new Wrap1('Hello' + i));
396e41f4b71Sopenharmony_ci        }
397e41f4b71Sopenharmony_ci    }
398e41f4b71Sopenharmony_ci    
399e41f4b71Sopenharmony_ci    build() {
400e41f4b71Sopenharmony_ci        Column() {
401e41f4b71Sopenharmony_ci            List() {
402e41f4b71Sopenharmony_ci                Repeat<Wrap1>(this.simpleList)
403e41f4b71Sopenharmony_ci                	.each((obj: RepeatItem<Wrap1>)=>{
404e41f4b71Sopenharmony_ci                    	ListItem() {
405e41f4b71Sopenharmony_ci                    		Row() {
406e41f4b71Sopenharmony_ci                    			Text('default index ' + obj.index + ': ')
407e41f4b71Sopenharmony_ci                            		.fontSize(30)
408e41f4b71Sopenharmony_ci                            	Text(obj.item.message)
409e41f4b71Sopenharmony_ci                            		.fontSize(30)
410e41f4b71Sopenharmony_ci                    		}
411e41f4b71Sopenharmony_ci                        }
412e41f4b71Sopenharmony_ci                        .margin(20)
413e41f4b71Sopenharmony_ci                	})
414e41f4b71Sopenharmony_ci                	.template('odd', (obj: RepeatItem<Wrap1>)=>{
415e41f4b71Sopenharmony_ci                    	ListItem() {
416e41f4b71Sopenharmony_ci                    		Row() {
417e41f4b71Sopenharmony_ci                    			Text('odd index ' + obj.index + ': ')
418e41f4b71Sopenharmony_ci                            		.fontSize(30)
419e41f4b71Sopenharmony_ci                            		.fontColor(Color.Blue)
420e41f4b71Sopenharmony_ci                            	Text(obj.item.message)
421e41f4b71Sopenharmony_ci                            		.fontSize(30)
422e41f4b71Sopenharmony_ci                            		.fontColor(Color.Blue)
423e41f4b71Sopenharmony_ci                    		}
424e41f4b71Sopenharmony_ci                        }
425e41f4b71Sopenharmony_ci                        .margin(20)
426e41f4b71Sopenharmony_ci                	})
427e41f4b71Sopenharmony_ci                	.template('even', (obj: RepeatItem<Wrap1>)=>{
428e41f4b71Sopenharmony_ci                    	ListItem() {
429e41f4b71Sopenharmony_ci                    		Row() {
430e41f4b71Sopenharmony_ci                    			Text('even index ' + obj.index + ': ')
431e41f4b71Sopenharmony_ci                            		.fontSize(30)
432e41f4b71Sopenharmony_ci                            		.fontColor(Color.Green)
433e41f4b71Sopenharmony_ci                            	Text(obj.item.message)
434e41f4b71Sopenharmony_ci                            		.fontSize(30)
435e41f4b71Sopenharmony_ci                            		.fontColor(Color.Green)
436e41f4b71Sopenharmony_ci                    		}
437e41f4b71Sopenharmony_ci                        }
438e41f4b71Sopenharmony_ci                        .margin(20)
439e41f4b71Sopenharmony_ci                	})
440e41f4b71Sopenharmony_ci                	.templateId((item: Wrap1, index: number) => {
441e41f4b71Sopenharmony_ci                		return index%2 ? 'odd' : 'even';
442e41f4b71Sopenharmony_ci                	})
443e41f4b71Sopenharmony_ci                	.key((item: Wrap1, index: number) => {
444e41f4b71Sopenharmony_ci                		return item.message;
445e41f4b71Sopenharmony_ci                	})
446e41f4b71Sopenharmony_ci            }
447e41f4b71Sopenharmony_ci            .cachedCount(5)
448e41f4b71Sopenharmony_ci            .width('100%')
449e41f4b71Sopenharmony_ci            .height('100%')
450e41f4b71Sopenharmony_ci        }
451e41f4b71Sopenharmony_ci        .height('100%')
452e41f4b71Sopenharmony_ci    }
453e41f4b71Sopenharmony_ci}
454e41f4b71Sopenharmony_ci```
455e41f4b71Sopenharmony_ci
456e41f4b71Sopenharmony_ci![Repeat-VirtualScroll-DataChange](./figures/Repeat-VirtualScroll-Template.gif)
457e41f4b71Sopenharmony_ci
458e41f4b71Sopenharmony_ci#### The GUI is rendered abnormally when the keys are the same.
459e41f4b71Sopenharmony_ci
460e41f4b71Sopenharmony_ciIf the duplicate keys are misused in the virtualScroll scenario, the GUI rendering is abnormal.
461e41f4b71Sopenharmony_ci
462e41f4b71Sopenharmony_ci```ts
463e41f4b71Sopenharmony_ci@Entry
464e41f4b71Sopenharmony_ci@ComponentV2
465e41f4b71Sopenharmony_cistruct RepeatKey {
466e41f4b71Sopenharmony_ci  @Local simpleList: Array<string> = [];
467e41f4b71Sopenharmony_ci
468e41f4b71Sopenharmony_ci  aboutToAppear(): void {
469e41f4b71Sopenharmony_ci    for (let i = 0; i < 200; i++) {
470e41f4b71Sopenharmony_ci      this.simpleList.push(`item ${i}`);
471e41f4b71Sopenharmony_ci    }
472e41f4b71Sopenharmony_ci  }
473e41f4b71Sopenharmony_ci
474e41f4b71Sopenharmony_ci  build() {
475e41f4b71Sopenharmony_ci    Column({ space: 10 }) {
476e41f4b71Sopenharmony_ci      List() {
477e41f4b71Sopenharmony_ci        Repeat<string>(this.simpleList)
478e41f4b71Sopenharmony_ci          .each((obj: RepeatItem<string>) => {
479e41f4b71Sopenharmony_ci            ListItem() {
480e41f4b71Sopenharmony_ci              Text(obj.item)
481e41f4b71Sopenharmony_ci                .fontSize(30)
482e41f4b71Sopenharmony_ci            }
483e41f4b71Sopenharmony_ci          })
484e41f4b71Sopenharmony_ci          .key((item: string, index: number) => {
485e41f4b71Sopenharmony_ci            return 'same key'; // Define the same key.
486e41f4b71Sopenharmony_ci          })
487e41f4b71Sopenharmony_ci          .virtualScroll({ totalCount: 200 })
488e41f4b71Sopenharmony_ci          .templateId((item:string, index: number) => 'default')
489e41f4b71Sopenharmony_ci          .template('default', (ri) => {
490e41f4b71Sopenharmony_ci            Text(ri.item)
491e41f4b71Sopenharmony_ci              .fontSize(30)
492e41f4b71Sopenharmony_ci          }, { cachedCount: 2 })
493e41f4b71Sopenharmony_ci      }
494e41f4b71Sopenharmony_ci      .cachedCount(2)
495e41f4b71Sopenharmony_ci      .border({ width: 1 })
496e41f4b71Sopenharmony_ci      .width('90%')
497e41f4b71Sopenharmony_ci      .height('70%')
498e41f4b71Sopenharmony_ci    }
499e41f4b71Sopenharmony_ci    .justifyContent(FlexAlign.Center)
500e41f4b71Sopenharmony_ci    .width('100%')
501e41f4b71Sopenharmony_ci    .height('100%')
502e41f4b71Sopenharmony_ci  }
503e41f4b71Sopenharmony_ci}
504e41f4b71Sopenharmony_ci```
505e41f4b71Sopenharmony_ci
506e41f4b71Sopenharmony_ciThe following figure shows the abnormal effect (the first data item **item 0** disappears).
507e41f4b71Sopenharmony_ci
508e41f4b71Sopenharmony_ci<img src="./figures/Repeat-VirtualScroll-Same-Key.jpg" width="300" />
509e41f4b71Sopenharmony_ci
510e41f4b71Sopenharmony_ci## FAQs
511e41f4b71Sopenharmony_ci
512e41f4b71Sopenharmony_ci### Ensure that the Position of the Scrollbar Remains Unchanged When the List Data Outside the Screen Changes
513e41f4b71Sopenharmony_ci
514e41f4b71Sopenharmony_ciDeclare the **Repeat** component in the **List** component to implement the **key** generation logic and **each** logic (as shown in the following sample code). Click **insert** to insert an element before the first element displayed on the screen, enabling the screen to scroll down.
515e41f4b71Sopenharmony_ci
516e41f4b71Sopenharmony_ci```ts
517e41f4b71Sopenharmony_ci// Define a class and mark it as observable.
518e41f4b71Sopenharmony_ci// Customize an array in the class and mark it as traceable.
519e41f4b71Sopenharmony_ci@ObservedV2
520e41f4b71Sopenharmony_ciclass ArrayHolder {
521e41f4b71Sopenharmony_ci  @Trace arr: Array<number> = [];
522e41f4b71Sopenharmony_ci
523e41f4b71Sopenharmony_ci  // constructor, used to initialize arrays.
524e41f4b71Sopenharmony_ci  constructor(count: number) {
525e41f4b71Sopenharmony_ci    for (let i = 0; i < count; i++) {
526e41f4b71Sopenharmony_ci      this.arr.push(i);
527e41f4b71Sopenharmony_ci    }
528e41f4b71Sopenharmony_ci  }
529e41f4b71Sopenharmony_ci}
530e41f4b71Sopenharmony_ci
531e41f4b71Sopenharmony_ci@Entry
532e41f4b71Sopenharmony_ci@ComponentV2
533e41f4b71Sopenharmony_ciexport struct RepeatTemplateSingle {
534e41f4b71Sopenharmony_ci  @Local arrayHolder: ArrayHolder = new ArrayHolder(100);
535e41f4b71Sopenharmony_ci  @Local totalCount: number = this.arrayHolder.arr.length;
536e41f4b71Sopenharmony_ci  scroller: Scroller = new Scroller();
537e41f4b71Sopenharmony_ci
538e41f4b71Sopenharmony_ci  build() {
539e41f4b71Sopenharmony_ci    Column({ space: 5 }) {
540e41f4b71Sopenharmony_ci      List({ space: 20, initialIndex: 19, scroller: this.scroller }) {
541e41f4b71Sopenharmony_ci        Repeat(this.arrayHolder.arr)
542e41f4b71Sopenharmony_ci          .virtualScroll({ totalCount: this.totalCount })
543e41f4b71Sopenharmony_ci          .templateId((item, index) => {
544e41f4b71Sopenharmony_ci            return 'number';
545e41f4b71Sopenharmony_ci          })
546e41f4b71Sopenharmony_ci          .template('number', (r) => {
547e41f4b71Sopenharmony_ci            ListItem() {
548e41f4b71Sopenharmony_ci              Text(r.index! + ":" + r.item + "Reuse");
549e41f4b71Sopenharmony_ci            }
550e41f4b71Sopenharmony_ci          })
551e41f4b71Sopenharmony_ci          .each((r) => {
552e41f4b71Sopenharmony_ci            ListItem() {
553e41f4b71Sopenharmony_ci              Text(r.index! + ":" + r.item + "eachMessage");
554e41f4b71Sopenharmony_ci            }
555e41f4b71Sopenharmony_ci          })
556e41f4b71Sopenharmony_ci      }
557e41f4b71Sopenharmony_ci      .height('30%')
558e41f4b71Sopenharmony_ci
559e41f4b71Sopenharmony_ci      Button(`insert totalCount ${this.totalCount}`)
560e41f4b71Sopenharmony_ci        .height(60)
561e41f4b71Sopenharmony_ci        .onClick(() => {
562e41f4b71Sopenharmony_ci          // Insert an element which locates in the previous position displayed on the screen.
563e41f4b71Sopenharmony_ci          this.arrayHolder.arr.splice(18, 0, this.totalCount);
564e41f4b71Sopenharmony_ci          this.totalCount = this.arrayHolder.arr.length;
565e41f4b71Sopenharmony_ci        })
566e41f4b71Sopenharmony_ci    }
567e41f4b71Sopenharmony_ci    .width('100%')
568e41f4b71Sopenharmony_ci    .margin({ top: 5 })
569e41f4b71Sopenharmony_ci  }
570e41f4b71Sopenharmony_ci}
571e41f4b71Sopenharmony_ci```
572e41f4b71Sopenharmony_ci
573e41f4b71Sopenharmony_ciThe figure below shows the effect.
574e41f4b71Sopenharmony_ci
575e41f4b71Sopenharmony_ci![Repeat-case1-Error](./figures/Repeat-Case1-Error.gif)
576e41f4b71Sopenharmony_ci
577e41f4b71Sopenharmony_ciIn some scenarios, if you do not want the data source change outside the screen to affect the position where the **Scroller** of the **List** stays on the screen, you can use the [onScrollIndex](https://gitee.com/openharmony/docs/blob/master/en/application-dev/ui/arkts-layout-development-create-list.md#responding-to-the-scrolling-position) of the **List** component to listen for the scrolling action. When the list scrolls, you can obtain the scrolling position of a list. Use the [scrollToIndex](https://gitee.com/openharmony/docs/blob/master/en/application-dev/reference/apis-arkui/arkui-ts/ts-container-scroll.md#scrolltoindex) feature of the **Scroller** component to slide to the specified **index** position. In this way, when data is added to or deleted from the data source outside the screen, the position where the **Scroller** stays remains unchanged.
578e41f4b71Sopenharmony_ci
579e41f4b71Sopenharmony_ciThe following code shows the case of adding data to the data source.
580e41f4b71Sopenharmony_ci
581e41f4b71Sopenharmony_ci```ts
582e41f4b71Sopenharmony_ci// Define a class and mark it as observable.
583e41f4b71Sopenharmony_ci// Customize an array in the class and mark it as traceable.
584e41f4b71Sopenharmony_ci@ObservedV2
585e41f4b71Sopenharmony_ciclass ArrayHolder {
586e41f4b71Sopenharmony_ci  @Trace arr: Array<number> = [];
587e41f4b71Sopenharmony_ci
588e41f4b71Sopenharmony_ci  // constructor, used to initialize arrays.
589e41f4b71Sopenharmony_ci  constructor(count: number) {
590e41f4b71Sopenharmony_ci    for (let i = 0; i < count; i++) {
591e41f4b71Sopenharmony_ci      this.arr.push(i);
592e41f4b71Sopenharmony_ci    }
593e41f4b71Sopenharmony_ci  }
594e41f4b71Sopenharmony_ci}
595e41f4b71Sopenharmony_ci
596e41f4b71Sopenharmony_ci@Entry
597e41f4b71Sopenharmony_ci@ComponentV2
598e41f4b71Sopenharmony_ciexport struct RepeatTemplateSingle {
599e41f4b71Sopenharmony_ci  @Local arrayHolder: ArrayHolder = new ArrayHolder(100);
600e41f4b71Sopenharmony_ci  @Local totalCount: number = this.arrayHolder.arr.length;
601e41f4b71Sopenharmony_ci  scroller: Scroller = new Scroller();
602e41f4b71Sopenharmony_ci
603e41f4b71Sopenharmony_ci  private start: number = 1;
604e41f4b71Sopenharmony_ci  private end: number = 1;
605e41f4b71Sopenharmony_ci
606e41f4b71Sopenharmony_ci  build() {
607e41f4b71Sopenharmony_ci    Column({ space: 5 }) {
608e41f4b71Sopenharmony_ci      List({ space: 20, initialIndex: 19, scroller: this.scroller }) {
609e41f4b71Sopenharmony_ci        Repeat(this.arrayHolder.arr)
610e41f4b71Sopenharmony_ci          .virtualScroll({ totalCount: this.totalCount })
611e41f4b71Sopenharmony_ci          .templateId((item, index) => {
612e41f4b71Sopenharmony_ci            return 'number';
613e41f4b71Sopenharmony_ci          })
614e41f4b71Sopenharmony_ci          .template('number', (r) => {
615e41f4b71Sopenharmony_ci            ListItem() {
616e41f4b71Sopenharmony_ci              Text(r.index! + ":" + r.item + "Reuse");
617e41f4b71Sopenharmony_ci            }
618e41f4b71Sopenharmony_ci          })
619e41f4b71Sopenharmony_ci          .each((r) => {
620e41f4b71Sopenharmony_ci            ListItem() {
621e41f4b71Sopenharmony_ci              Text(r.index! + ":" + r.item + "eachMessage");
622e41f4b71Sopenharmony_ci            }
623e41f4b71Sopenharmony_ci          })
624e41f4b71Sopenharmony_ci      }
625e41f4b71Sopenharmony_ci      .onScrollIndex((start, end) => {
626e41f4b71Sopenharmony_ci        this.start = start;
627e41f4b71Sopenharmony_ci        this.end = end;
628e41f4b71Sopenharmony_ci      })
629e41f4b71Sopenharmony_ci      .height('30%')
630e41f4b71Sopenharmony_ci
631e41f4b71Sopenharmony_ci      Button(`insert totalCount ${this.totalCount}`)
632e41f4b71Sopenharmony_ci        .height(60)
633e41f4b71Sopenharmony_ci        .onClick(() => {
634e41f4b71Sopenharmony_ci          // Insert an element which locates in the previous position displayed on the screen.
635e41f4b71Sopenharmony_ci          this.arrayHolder.arr.splice(18, 0, this.totalCount);
636e41f4b71Sopenharmony_ci          let rect = this.scroller.getItemRect(this.start); // Obtain the size and position of the child component.
637e41f4b71Sopenharmony_ci          this.scroller.scrollToIndex(this.start + 1); // Slide to the specified index.
638e41f4b71Sopenharmony_ci          this.scroller.scrollBy(0, -rect.y); // Slide by a specified distance.
639e41f4b71Sopenharmony_ci          this.totalCount = this.arrayHolder.arr.length;
640e41f4b71Sopenharmony_ci        })
641e41f4b71Sopenharmony_ci    }
642e41f4b71Sopenharmony_ci    .width('100%')
643e41f4b71Sopenharmony_ci    .margin({ top: 5 })
644e41f4b71Sopenharmony_ci  }
645e41f4b71Sopenharmony_ci}
646e41f4b71Sopenharmony_ci```
647e41f4b71Sopenharmony_ci
648e41f4b71Sopenharmony_ciThe figure below shows the effect.
649e41f4b71Sopenharmony_ci
650e41f4b71Sopenharmony_ci![Repeat-case1-Succ](./figures/Repeat-Case1-Succ.gif)
651e41f4b71Sopenharmony_ci
652e41f4b71Sopenharmony_ci### The totalCount Value Is Greater Than the Length of Data Source
653e41f4b71Sopenharmony_ci
654e41f4b71Sopenharmony_ciWhen the total length of the data source is large, the lazy loading is used to load some data first. To enable **Repeat** to display the correct scrollbar style, you need to change the value of **totalCount** to the total length of data. That is, before all data sources are loaded, the value of **totalCount** is greater than that of **array.length**.
655e41f4b71Sopenharmony_ci
656e41f4b71Sopenharmony_ciWhen the **Repeat** component is initialized, the application must provide sufficient data items for rendering. During the scrolling process of the parent container, the application needs to execute the request instruction for subsequent data items before rendering to ensure that no blank area is displayed during the list sliding process until all data sources are loaded.
657e41f4b71Sopenharmony_ci
658e41f4b71Sopenharmony_ciYou can use the callback of [onScrollIndex](https://gitee.com/openharmony/docs/blob/master/en/application-dev/ui/arkts-layout-development-create-list.md#controlling-the-scrolling-position) attribute of the **List** or **Grid** parent component to implement the preceding specification. The sample code is as follows:
659e41f4b71Sopenharmony_ci
660e41f4b71Sopenharmony_ci```ts
661e41f4b71Sopenharmony_ci@ObservedV2
662e41f4b71Sopenharmony_ciclass VehicleData {
663e41f4b71Sopenharmony_ci  @Trace name: string;
664e41f4b71Sopenharmony_ci  @Trace price: number;
665e41f4b71Sopenharmony_ci
666e41f4b71Sopenharmony_ci  constructor(name: string, price: number) {
667e41f4b71Sopenharmony_ci    this.name = name;
668e41f4b71Sopenharmony_ci    this.price = price;
669e41f4b71Sopenharmony_ci  }
670e41f4b71Sopenharmony_ci}
671e41f4b71Sopenharmony_ci
672e41f4b71Sopenharmony_ci@ObservedV2
673e41f4b71Sopenharmony_ciclass VehicleDB {
674e41f4b71Sopenharmony_ci  public vehicleItems: VehicleData[] = [];
675e41f4b71Sopenharmony_ci
676e41f4b71Sopenharmony_ci  constructor() {
677e41f4b71Sopenharmony_ci    // init data size 20
678e41f4b71Sopenharmony_ci    for (let i = 1; i <= 20; i++) {
679e41f4b71Sopenharmony_ci      this.vehicleItems.push(new VehicleData(`Vehicle${i}`, i));
680e41f4b71Sopenharmony_ci    }
681e41f4b71Sopenharmony_ci  }
682e41f4b71Sopenharmony_ci}
683e41f4b71Sopenharmony_ci
684e41f4b71Sopenharmony_ci@Entry
685e41f4b71Sopenharmony_ci@ComponentV2
686e41f4b71Sopenharmony_cistruct entryCompSucc {
687e41f4b71Sopenharmony_ci  @Local vehicleItems: VehicleData[] = new VehicleDB().vehicleItems;
688e41f4b71Sopenharmony_ci  @Local listChildrenSize: ChildrenMainSize = new ChildrenMainSize(60);
689e41f4b71Sopenharmony_ci  @Local totalCount: number = this.vehicleItems.length;
690e41f4b71Sopenharmony_ci  scroller: Scroller = new Scroller();
691e41f4b71Sopenharmony_ci
692e41f4b71Sopenharmony_ci  build() {
693e41f4b71Sopenharmony_ci    Column({ space: 3 }) {
694e41f4b71Sopenharmony_ci      List({ scroller: this.scroller }) {
695e41f4b71Sopenharmony_ci        Repeat(this.vehicleItems)
696e41f4b71Sopenharmony_ci          .virtualScroll({ totalCount: 50 }) // total data size 50
697e41f4b71Sopenharmony_ci          .templateId(() => 'default')
698e41f4b71Sopenharmony_ci          .template('default', (ri) => {
699e41f4b71Sopenharmony_ci            ListItem() {
700e41f4b71Sopenharmony_ci              Column() {
701e41f4b71Sopenharmony_ci                Text(`${ri.item.name} + ${ri.index}`)
702e41f4b71Sopenharmony_ci                  .width('90%')
703e41f4b71Sopenharmony_ci                  .height(this.listChildrenSize.childDefaultSize)
704e41f4b71Sopenharmony_ci                  .backgroundColor(0xFFA07A)
705e41f4b71Sopenharmony_ci                  .textAlign(TextAlign.Center)
706e41f4b71Sopenharmony_ci                  .fontSize(20)
707e41f4b71Sopenharmony_ci                  .fontWeight(FontWeight.Bold)
708e41f4b71Sopenharmony_ci              }
709e41f4b71Sopenharmony_ci            }.border({ width: 1 })
710e41f4b71Sopenharmony_ci          }, { cachedCount: 5 })
711e41f4b71Sopenharmony_ci          .each((ri) => {
712e41f4b71Sopenharmony_ci            ListItem() {
713e41f4b71Sopenharmony_ci              Text("Wrong: " + `${ri.item.name} + ${ri.index}`)
714e41f4b71Sopenharmony_ci                .width('90%')
715e41f4b71Sopenharmony_ci                .height(this.listChildrenSize.childDefaultSize)
716e41f4b71Sopenharmony_ci                .backgroundColor(0xFFA07A)
717e41f4b71Sopenharmony_ci                .textAlign(TextAlign.Center)
718e41f4b71Sopenharmony_ci                .fontSize(20)
719e41f4b71Sopenharmony_ci                .fontWeight(FontWeight.Bold)
720e41f4b71Sopenharmony_ci            }.border({ width: 1 })
721e41f4b71Sopenharmony_ci          })
722e41f4b71Sopenharmony_ci          .key((item, index) => `${index}:${item}`)
723e41f4b71Sopenharmony_ci      }
724e41f4b71Sopenharmony_ci      .height('50%')
725e41f4b71Sopenharmony_ci      .margin({ top: 20 })
726e41f4b71Sopenharmony_ci      .childrenMainSize(this.listChildrenSize)
727e41f4b71Sopenharmony_ci      .alignListItem(ListItemAlign.Center)
728e41f4b71Sopenharmony_ci      .onScrollIndex((start, end) => {
729e41f4b71Sopenharmony_ci        console.log('onScrollIndex', start, end);
730e41f4b71Sopenharmony_ci        // lazy data loading
731e41f4b71Sopenharmony_ci        if (this.vehicleItems.length < 50) {
732e41f4b71Sopenharmony_ci          for (let i = 0; i < 10; i++) {
733e41f4b71Sopenharmony_ci            if (this.vehicleItems.length < 50) {
734e41f4b71Sopenharmony_ci              this.vehicleItems.push(new VehicleData("Vehicle_loaded", i));
735e41f4b71Sopenharmony_ci            }
736e41f4b71Sopenharmony_ci          }
737e41f4b71Sopenharmony_ci        }
738e41f4b71Sopenharmony_ci      })
739e41f4b71Sopenharmony_ci    }
740e41f4b71Sopenharmony_ci  }
741e41f4b71Sopenharmony_ci}
742e41f4b71Sopenharmony_ci```
743e41f4b71Sopenharmony_ci
744e41f4b71Sopenharmony_ciThe figure below shows the effect.
745e41f4b71Sopenharmony_ci
746e41f4b71Sopenharmony_ci![Repeat-Case2-Succ](./figures/Repeat-Case2-Succ.gif)
747