• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# \@ReusableV2 Decorator: Reusing Components
2
3To reduce the performance overhead caused by repeated creation and destruction of custom components, you can use \@ReusableV2 to decorate \@ComponentV2 decorated custom components.
4
5Before reading this topic, you are advised to read [\@Reusable Decorator: Reusing Components](./arkts-reusable.md).
6
7>**NOTE**
8>
9>The \@ReusableV2 decorator is supported since API version 18.
10>
11
12## Overview
13
14Using \@ReusableV2 to decorate the custom components of V2 indicates that these components are reusable.
15
16- \@ReusableV2 can decorate only custom components of V2, that is, custom components decorated by \@ComponentV2. In addition, only \@ReusableV2 decorated custom components can be used as child components of custom components of V2.
17- \@ReusableV2 also provides the **aboutToRecycle** and **aboutToReuse** callbacks, which are called respectively when a component is recycled and reused. However, different from \@Reusable, **aboutToReuse** has no input parameters.
18- In the recycling phase, **aboutToRecycle** of all child components is recursively called (even if the child components are not reusable). In the reuse phase, so does **aboutToReuse** of all child components.
19- The custom component decorated by \@ReusableV2 remains frozen during recycling. That is, the UI r-render and the \@Monitor callback cannot be triggered. Different from the **freezeWhenInactive** flag, the delayed re-render is not triggered after the \@ReusableV2 decorated custom component is unfrozen.
20- When a \@ReusableV2 decorated custom component is reused, the value of its state variable is automatically reset, and the \@Computed and related \@Monitor in the component are recomputed. You are not advised to change the state variable of a component in **aboutToRecycle**. For details, see [Resetting State Variables in Components Before Reuse](#resetting-state-variables-in-components-before-reuse).
21- The reusable components of V1 and V2 can be used together under certain rules. For details, see the second point in [Constraints](#constraints).
22- Do not overuse the \@ReusableV2 decorator. Otherwise, the reuse efficiency may be undermined and the memory overhead may increase.
23
24## Decorator Description
25
26| \@ReusableV2 Decorator| Description                         |
27| ------------------ | ----------------------------- |
28| Parameters        | None                           |
29| Decorable components      | \@ComponentV2 decorated custom components|
30| Function        | Indicates that a component is reusable.           |
31
32```ts
33@ReusableV2 // Decorates @ComponentV2 decorated custom components.
34@ComponentV2
35struct ReusableV2Component {
36  @Local message: string = 'Hello World';
37  build () {
38  	Column() {
39  	  Text(this.message)
40  	}
41  }
42}
43```
44
45## Available APIs
46
47### ReuseIdCallback
48
49**ReuseIdCallback** calculates **reuseId**, whose type is **() => string**.
50
51### ReuseOptions
52
53**ReuseOptions** stores the **reuseId** information, which is used in the **.reuse** attribute.
54
55| Name   | Type           | Description                 |
56| ------- | --------------- | --------------------- |
57| reuseId | ReuseIdCallback | Callback used to obtain **reuseId**.|
58
59### The reuse Attribute
60
61The **reuse** attribute is used to specify a reuse ID for the custom component decorated by \@ReusableV2. Custom components with the same reuse ID will be reused. If reuse ID is not specified, the custom component name is used as reuse ID.
62
63| Parameter   | Type        | Description                                                  |
64| ------- | ------------ | ------------------------------------------------------ |
65| options | ReuseOptions | Configuration options for **reuse**, for example, **{reuseId: () => 'reuseComponent'}**.|
66
67```ts
68@Entry
69@ComponentV2
70struct Index {
71  build() {
72    Column() {
73      ReusableV2Component()
74        .reuse({reuseId: () => 'reuseComponent'}) // Use 'reuseComponent' as reuseId.
75      ReusableV2Component()
76        .reuse({reuseId: () => ''}) // If an empty string is used, the component name 'ReusableV2Component' is used as reuseId by default.
77      ReusableV2Component() // If reuseId is not specified, the component name 'ReusableV2Component' is used as reuseId by default.
78    }
79  }
80}
81@ReusableV2
82@ComponentV2
83struct ReusableV2Component {
84  build() {
85  }
86}
87```
88
89## Constraints
90
91- Only \@ReusableV2 decorated custom components can be used as child components of custom components of V2. If a reusable component of V2 is used in the custom components of V1, an error is reported during compilation. In complex scenarios where verification cannot be performed during compilation, an error is reported during runtime.
92
93  ```ts
94  @Entry
95  @ComponentV2
96  struct Index {
97    build() {
98      Column() {
99        ReusableV2Component() // Correct usage.
100        V1Component()
101      }
102    }
103  }
104  @ReusableV2
105  @ComponentV2
106  struct ReusableV2Component {
107    build() {
108    }
109  }
110  @Builder
111  function V2ReusableBuilder() {
112    ReusableV2Component()
113  }
114  @Component
115  struct V1Component {
116    build() {
117      Column() {
118        ReusableV2Component() // Incorrect usage. An error is reported during compilation.
119        V2ReusableBuilder() // Incorrect usage. An error is reported during runtime in complex scenarios.
120      }
121    }
122  }
123  ```
124
125- The following describes some mixed use cases of V1 and V2.
126
127  The mapping between a component and its type is listed in the table below.
128
129  | Component      | Type                        |
130  | ---------- | ------------------------------------ |
131  | Common component of V1| @Component decorated struct              |
132  | Common component of V2| @ComponentV2 decorated struct            |
133  | Reusable component of V1| @Reusable and @Component decorated struct    |
134  | Reusable component of V2| @ReusableV2 and @ComponentV2 decorated struct|
135
136  The following table shows the mixed use support between V1 and V2, indicating whether components in the second to fifth columns can be used as child components of the parent components in the first column.
137
138  For example, the common component of V1, common component of V2, and reusable component of V1 can be used as child components of the common component of V1 except the reusable component of V2.
139
140  |            | Common Component of V1| Common Component of V2|               Reusable Component of V1              |    Reusable Component of V2   |
141  | ---------- | :--------: | :--------: | :------------------------------------: | :--------------: |
142  | Common component of V1|    Yes   |    Yes   |                  Yes                 | No. An error is reported during compilation.|
143  | Common component of V2|    Yes   |    Yes   | No. An error is reported during compilation. The actual used child component is not created.|       Yes      |
144  | Reusable component of V1|    Yes   |    Yes   |                  Yes                 | No. An error is reported during compilation.|
145  | Reusable component of V2|    Yes   |    Yes   |            No. An error is reported during compilation.           |       Yes      |
146
147  According to the preceding table, only 12 possible parent-child relationships are supported. You are advised not to highly nest reusable components, because it will reduce the reuse efficiency.
148
149- The reusable components of V2 cannot be directly used in the **template** of **Repeat**, but can be used in the custom component of V2 in **template**.
150
151  ```ts
152  @Entry
153  @ComponentV2
154  struct Index {
155    @Local arr: number[] = [1, 2, 3, 4, 5];
156    build() {
157      Column() {
158        List() {
159          Repeat(this.arr)
160            .each(() => {})
161            .virtualScroll()
162            .templateId(() => 'a')
163            .template('a', (ri) => {
164              ListItem() {
165                Column() {
166                  ReusableV2Component({ val: ri.item}) // Usage not supported. An error is reported during compilation.
167                  ReusableV2Builder(ri.item) // Usage not supported. An error is reported during runtime.
168                  NormalV2Component({ val: ri.item}) // Reusable components of V2 can be used in common custom components of V2.
169                }
170              }
171            })
172        }
173      }
174    }
175  }
176  @ComponentV2
177  struct NormalV2Component {
178    @Require @Param val: number;
179    build() {
180      ReusableV2Component({ val: this.val })
181    }
182  }
183  @Builder
184  function ReusableV2Builder(param: number) {
185    ReusableV2Component({ val: param })
186  }
187  @ReusableV2
188  @ComponentV2
189  struct ReusableV2Component {
190    @Require @Param val: number;
191    build() {
192      Column() {
193        Text(`val: ${this.val}`)
194      }
195    }
196  }
197  ```
198
199## Lifecycle of Recycling and Reuse
200
201\@ReusableV2 provides the **aboutToRecycle** and **aboutToReuse** callbacks, which are called respectively when a component is recycled and reused.
202
203The following uses the **if** statement as an example:
204
205```ts
206@Entry
207@ComponentV2
208struct Index {
209  @Local condition1: boolean = false;
210  @Local condition2: boolean = true;
211  build() {
212    Column() {
213      Button('step1. appear')
214        .onClick(() => {
215          this.condition1 = true;
216        })
217      Button('step2. recycle')
218        .onClick(() => {
219          this.condition2 = false;
220        })
221      Button('step3. reuse')
222        .onClick(() => {
223          this.condition2 = true;
224        })
225      Button('step4. disappear')
226        .onClick(() => {
227          this.condition1 = false;
228        })
229      if (this.condition1) {
230        NormalV2Component({ condition: this.condition2 })
231      }
232    }
233  }
234}
235@ComponentV2
236struct NormalV2Component {
237  @Require @Param condition: boolean;
238  build() {
239    if (this.condition) {
240      ReusableV2Component()
241    }
242  }
243}
244@ReusableV2
245@ComponentV2
246struct ReusableV2Component {
247  aboutToAppear() {
248    console.log('ReusableV2Component aboutToAppear called'); // Called when a component is created.
249  }
250  aboutToDisappear() {
251    console.log('ReusableV2Component aboutToDisappear called'); // Called when a component is destroyed.
252  }
253  aboutToRecycle() {
254    console.log('ReusableV2Component aboutToRecycle called'); // Called when a component is recycled.
255  }
256  aboutToReuse() {
257    console.log('ReusableV2Component aboutToReuse called'); // Called when a component is reused.
258  }
259  build() {
260    Column() {
261      Text('ReusableV2Component')
262    }
263  }
264}
265```
266
267You are advised to follow the steps below:
268
2691. Click **step1. appear** to change the value of **condition1** to **true**. The **if** component in **Index** switches the branch and creates a **NormalV2Component**. Due to the initial value **true** of **condition2**, the **if** condition in **NormalV2Component** is met and the system attempts to create a **ReusableV2Component**. In this case, there is no element in the reuse pool. Therefore, **ReusableV2Component** is created, the **aboutToAppear** method is called back, and the log **ReusableV2Component aboutToAppear called** is output.
2702. Click **step2. recycle** to change the value of **condition2** to **false** and synchronize this change to **NormalV2Component** through \@Param. The **if** condition is switched. Because **ReusableV2Component** uses \@ReusableV2, the component is recycled to the reuse pool instead of being destroyed. The **aboutToRecycle** method is called back and the log **ReusableV2Component aboutToRecycle called** is output.
2713. Click **step3. reuse** to change the value of **condition2** to **true** and synchronize this change to **NormalV2Component** through \@Param. The **if** condition is switched. Because **ReusableV2Component** uses \@ReusableV2, the system attempts to search for the component in the reuse pool when creating the component. In this case, the component recycled in step 2 is reused from the reuse pool, the **aboutToReuse** method is called back, and the log **ReusableV2Component aboutToReuse called** is output.
2724. Click **step4. disappear** to change the value of **condition1** to **false**. The **if** component in **Index** switches the branch and destroys **NormalV2Component**. In this case, **ReusableV2Component** is destroyed due to the destroyed parent component, the **aboutToDisappear** method is called back, and the log **ReusableV2Component aboutToDisappear called** is output.
273
274If the reusable component has child components, **aboutToRecycle** and **aboutToReuse** of the child components are recursively called during recycling and reuse (irrelevant to whether the child components are marked for reuse) until all child components are traversed.
275
276## Component Freezing in the Reuse Phase
277
278In the previous reuse, components of V1 can still respond to updates in the reuse pool, which has a negative impact on performance. Therefore, you need to use the component freezing capability to resolve this issue. For this, V2 components are automatically frozen when reused and do not respond to changes that occur during recycling. For example, changes in **aboutToRecycle** are not updated to the UI, and \@Computed and \@Monitor are not triggered. The frozen state lasts until **aboutToReuse** and subsequent variables change. In this case, the UI re-render, \@Computed recalculation, and \@Monitor are triggered.
279
280The following uses the **if** statement as an example:
281
282```ts
283@ObservedV2
284class Info {
285  @Trace age: number = 25;
286}
287const info: Info = new Info();
288@Entry
289@ComponentV2
290struct Index {
291  @Local condition: boolean = true;
292  build() {
293    Column() {
294      Button('Reuse/Recycle').onClick(()=>{this.condition=!this.condition;})
295      Button('Change value').onClick(()=>{info.age++;})
296      if (this.condition) {
297        ReusableV2Component()
298      }
299    }
300  }
301}
302@ReusableV2
303@ComponentV2
304struct ReusableV2Component {
305  @Local info: Info = info; // This is used only for demonstration. You are not advised to assign a value to the global variable using @Local.
306  @Monitor('info.age')
307  onValChange() {
308    console.log('info.age change');
309  }
310  aboutToRecycle() {
311    console.log('aboutToRecycle');
312    this.info.age++;
313  }
314  aboutToReuse() {
315    console.log('aboutToReuse');
316    this.info.age++;
317  }
318  onRender(): string {
319    console.log('info.age onRender')
320    return this.info.age.toString();
321  }
322  build() {
323    Column() {
324      Text(this.onRender())
325    }
326  }
327}
328```
329
330You are advised to follow the steps below:
331
3321. Click the **Change value** button to listen for the UI change. \@Monitor is triggered to output the logs **info.age change** and **info.age onRender**, indicating that the change can be listened for and the UI re-render can be triggered.
3332. Click the **Reuse/Recycle** button to call the **aboutToRecycle** method and output the log of **aboutToRecycle**. However, \@Monitor is not triggered and the **onRender** method is not called back.
3343. Click the **Change value** button. The UI is not re-rendered, \@Monitor is not triggered, and the **onRender** method is not called back.
3354. Click the **Reuse/Recycle** button. The UI is re-rendered, \@Monitor is trigger to output the log **info.age change**, and the **onRender** method is called back to output the log **info.age onRender**.
336
337If the auto-increment operation is removed from the **aboutToReuse** method, the \@Monitor callback is not triggered in step 4.
338
339In complex mixed use scenarios, the rules for component freezing can be summarized as follows:
340
3411. For Components of V1, component freezing is determined by **freezeWhenInactive**.
3422. V2 components are automatically frozen.
343
344## Resetting State Variables in Components Before Reuse
345
346Different from \@Reusable, \@ReusableV2 resets the state variables and related \@Computed and \@Monitor content in components before reuse. During the reuse, all custom components of V2 are reset no matter whether they are decorated with \@ReusableV2.
347
348The reset is performed based on the sequence of variables defined in the component according to the following rules.
349
350| Decorator    | Method for Resetting                                                    |
351| ---------- | ------------------------------------------------------------ |
352| \@Local    | Use the initial value for reassignment.                            |
353| \@Param    | Use external input value for reassignment. If there is no external input value, use the local initial value. Note that variables decorated by \@Once are also reset and initialized.|
354| \@Event    | Use external input value for reassignment. If there is no external input value, use the local initial value. If there is no local initial value, the default empty implementation is generated.|
355| \@Provider | Use the initial value for reassignment.                            |
356| \@Consumer | If the corresponding \@Provider exists, use the value of \@Provider. Otherwise, use the local initial value for reassignment.|
357| \@Computed | The latest value is used for recalculation. If the used variable is not reset, the value before the reset is used. Therefore, you are advised to define \@Computed after the used variable.|
358| \@Monitor  | Triggered after all the preceding variables are reset. The variable change generated during the reset does not trigger the \@Monitor callback. Only the **before** value in **IMonitorValue** is updated. Values that do not change during the reset do not trigger the reset of \@Monitor.|
359| Constant      | Read-only constants are included and are not reset.                                |
360
361The following example shows some of the effects:
362
363```ts
364@ObservedV2
365class Info {
366  @Trace age: number;
367  constructor(age: number) {
368    this.age = age;
369  }
370}
371@Entry
372@ComponentV2
373struct Index {
374  @Local local: number = 0;
375  @Provider('inherit') inheritProvider: number = 100;
376  @Local condition: boolean = true;
377  build() {
378    Column() {
379      Button('Recycle/Reuse').onClick(()=>{this.condition=!this.condition;})
380      Column() {
381        Text('Variables of parent component')
382        Text(`local: ${this.local}`).onClick(()=>{this.local++;})
383        Text(`inheritProvider: ${this.inheritProvider}`).onClick(()=>{this.inheritProvider++;})
384      }.borderWidth(2)
385      if (this.condition) {
386        ReusableV2Component({
387          paramOut: this.local,
388          paramOnce: this.local,
389          changeParam: () => {
390            this.local++;
391          }
392        })
393      }
394    }
395  }
396}
397@ReusableV2
398@ComponentV2
399struct ReusableV2Component {
400  @Local val: number = 0;
401  @Local info: Info = new Info(25);
402  @Param paramLocal: number = 1;
403  @Require @Param paramOut: number;
404  @Require @Param @Once paramOnce: number;
405  @Event changeParam: () => void;
406  @Provider('selfProvider') selfProvider: number = 0;
407  @Consumer('inherit') inheritConsumer: number = 0;
408  @Consumer('selfConsumer') selfConsumer: number = 0;
409  noDecoVariable: number = 0; // No decorator is used. It is used as a constant.
410  noDecoInfo: Info = new Info(30); // No decorator is used. It is used as a constant.
411  readonly readOnlyVariable: number = 0; // Read-only constant.
412  @Computed
413  get plusParam() {
414    return this.paramLocal + this.paramOut + this.paramOnce;
415  }
416  @Monitor('val')
417  onValChange(monitor: IMonitor) {
418    console.log(`val change from ${monitor.value()?.before} to ${monitor.value()?.now}`);
419  }
420  @Monitor('plusParam')
421  onPlusParamChange(monitor: IMonitor) {
422    console.log(`plusParam change from ${monitor.value()?.before} to ${monitor.value()?.now}`);
423  }
424  build() {
425    Column() {
426	  Column() {
427        Text('Variables reset to local initial values')
428        Text(`val: ${this.val}`).onClick(()=>{this.val++;})
429        Text(`info.age: ${this.info.age}`).onClick(()=>{this.info.age++;})
430        Text(`paramLocal: ${this.paramLocal}`).onClick(()=>{/* Local without external input value fails to be changed locally. */})
431        Text(`selfProvider: ${this.selfProvider}`).onClick(()=>{this.selfProvider++;})
432        Text(`selfConsumer: ${this.selfConsumer}`).onClick(()=>{this.selfConsumer++;})
433      }.borderWidth(2)
434      Column() {
435        Text('Reset to an external variable')
436        Text(`paramOut: ${this.paramOut}`).onClick(()=>{this.changeParam();})
437        Text(`paramOnce: ${this.paramOnce}`).onClick(()=>{this.paramOnce++;})
438      }.borderWidth(2)
439      Column() {
440        Text ('Depending on the parent component')
441        Text(`inheritConsumer: ${this.inheritConsumer}`).onClick(()=>{this.inheritConsumer++;})
442        Text(`plusParam: ${this.plusParam}`)
443      }.borderWidth(2)
444      Column() {
445        Text('Not reset')
446        Text(`noDecoVariable: ${this.noDecoVariable}`)
447        Text(`noDecoInfo.age: ${this.noDecoInfo.age}`).onClick(()=>{this.noDecoInfo.age++;}) // Update can be triggered but the variable is not reset when component is reused.
448        Text(`readOnlyVariable: ${this.readOnlyVariable}`)
449      }.borderWidth(2)
450    }
451  }
452}
453```
454
455You can click each variable and click **Recycle/Reuse** to view the reset state after reuse.
456
457In the preceding example, **noDecoInfo** is not reset. If a \@Monitor is used to listen for **noDecoInfo.age**, it will not be reset because **noDecoInfo** does not change. Therefore, when **noDecoInfo.age** is changed for the first time, the **before** value of **IMonitorValue** will not be reset, which is still the value before reuse.
458
459The simplified example is as follows:
460
461```ts
462@ObservedV2
463class Info {
464  @Trace age: number;
465  constructor(age: number) {
466    this.age = age;
467  }
468}
469@Entry
470@ComponentV2
471struct Index {
472  @Local condition: boolean = true;
473  build() {
474    Column() {
475      Button('Recycle/Reuse').onClick(()=>{this.condition=!this.condition;})
476      if (this.condition) {
477        ReusableV2Component()
478      }
479    }
480  }
481}
482@ReusableV2
483@ComponentV2
484struct ReusableV2Component {
485  noDecoInfo: Info = new Info(30); // No decorator is used. It is used as a constant.
486  @Monitor('noDecoInfo.age')
487  onAgeChange(monitor: IMonitor) {
488    console.log(`age change from ${monitor.value()?.before} to ${monitor.value()?.now}`);
489  }
490  aboutToRecycle() {
491    this.noDecoInfo.age = 25;
492  }
493  aboutToReuse() {
494    this.noDecoInfo.age = 35;
495  }
496  build() {
497    Column() {
498	  Column() {
499        Text(`noDecoInfo.age: ${this.noDecoInfo.age}`)
500          .onClick(()=>{this.noDecoInfo.age++;}) // Update can be triggered but the variable is not reset.
501      }
502    }
503  }
504}
505```
506
507You are advised to follow the steps below:
508
5091. Click **noDecoInfo.age: 30**. The UI is re-rendered to **noDecoInfo.age: 31** and \@Monitor is triggered to output the log **age change from 30 to 31**.
5102. Click **Recycle/Reuse** twice. The UI is re-rendered to **noDecoInfo.age: 35** and \@Monitor is triggered to output the log **age change from 31 to 35**.
5113. Click **noDecoInfo.age: 35**. The UI is re-rendered to **noDecoInfo.age: 36** and \@Monitor is triggered to output the log **age change from 35 to 36**.
512
513Due to the freezing mechanism, the value change in **aboutToRecycle** is not listened by \@Monitor. After the variable is reset, a new value is assigned to the variable. Therefore, for the state variable in the component, the value assignment in **aboutToRecycle** does not have obvious effect. For the constant (such as **noDecoInfo**), due to the freezing mechanism, the change of **age** in **aboutToRecycle** is not observable and cannot be reset, therefore, the \@Monitor bound to **age** will not be reset. Finally, in \@Monitor called back in step 2, the value of **monitor.value()?.before** is **31** instead of the initial value **30** of **age**.
514
515As such, you are advised to reduce the use of constant objects that contain the \@Trace attribute in reuse scenarios to ensure that the reuse meets expectations.
516
517## When to Use
518
519### Using in the if Component
520
521Change the conditions of the **if** component to control component recycling or reuse.
522
523```ts
524@Entry
525@ComponentV2
526struct Index {
527  @Local condition: boolean = true;
528  build() {
529    Column() {
530      Button('Recycle/Reuse').onClick(()=>{this.condition=!this.condition;}) // Click to switch the recycle/reuse state.
531      if (this.condition) {
532        ReusableV2Component()
533      }
534    }
535  }
536}
537@ReusableV2
538@ComponentV2
539struct ReusableV2Component {
540  @Local message: string = 'Hello World';
541  aboutToRecycle() {
542    console.log('ReusableV2Component aboutToRecycle'); // Called when a component is recycled.
543  }
544  aboutToReuse() {
545    console.log('ReusableV2Component aboutToReuse'); // Called when a component is reused.
546  }
547  build() {
548    Column() {
549      Text(this.message)
550    }
551  }
552}
553```
554
555### Using in the each Attribute of Repeat in the Non-virtualScroll Scenarios
556
557In the non-virtualScroll scenarios of the **Repeat** component, recycling or reuse is triggered when a subtree is deleted or created.
558
559```ts
560@Entry
561@ComponentV2
562struct Index {
563  @Local simpleList: number[] = [1, 2, 3, 4, 5];
564  @Local condition: boolean = true;
565  build() {
566    Column() {
567      Button('Delete/Create Repeat').onClick(()=>{this.condition=!this.condition;})
568      Button('Add element').onClick(()=>{this.simpleList.push(this.simpleList.length+1);})
569      Button('Delete element').onClick(()=>{this.simpleList.pop();})
570      Button('Change element').onClick(()=>{this.simpleList[0]++;})
571      if (this.condition) {
572        List({ space: 10 }) {
573          Repeat(this.simpleList)
574            .each((obj: RepeatItem<number>) => {
575              ListItem() {
576                Column() {
577                  ReusableV2Component({ num: obj.item })
578                }
579              }
580            })
581        }
582      }
583    }
584  }
585}
586@ReusableV2
587@ComponentV2
588struct ReusableV2Component {
589  @Require @Param num: number;
590  aboutToAppear() {
591    console.log('ReusableV2Component aboutToAppear');
592  }
593  aboutToRecycle() {
594    console.log('ReusableV2Component aboutToRecycle');
595  }
596  aboutToReuse() {
597    console.log('ReusableV2Component aboutToReuse');
598  }
599  build() {
600    Column() {
601      Text(`${this.num}`)
602    }
603  }
604}
605```
606
607### Using in the each Attribute of Repeat in the virtualScroll Scenarios
608
609In the virtualScroll scenarios of the **Repeat** component, the cache pool of **Repeat** is preferentially used. In the normal sliding and update scenarios, component recycling and reuse are not involved. When the cache pool needs to be expanded, new child components are required from the custom components. In this case, if there are reusable nodes in the reuse pool, the nodes will be reused.
610
611In the following example, click **Change condition** to add three nodes to the reuse pool. When you slide down the **List** component, you can see the log output **ReusableV2Component aboutToReuse**, indicating that **Repeat** can use the reuse pool of the custom component to fill its cache pool.
612
613```ts
614@Entry
615@ComponentV2
616struct Index {
617  @Local condition: boolean = true;
618  @Local simpleList: number[] = [];
619  aboutToAppear(): void {
620    for (let i = 0; i < 100; i++) {
621      this.simpleList.push(i)
622    }
623  }
624  build() {
625    Column() {
626      Button('Change condition').onClick(()=>{this.condition=!this.condition;})
627      if (this.condition) {
628        // This is only for demonstration. Fill three components in the reuse pool.
629        ReusableV2Component({ num: 0})
630        ReusableV2Component({ num: 0})
631        ReusableV2Component({ num: 0})
632      }
633      List({ space: 10 }) {
634        Repeat(this.simpleList)
635          .virtualScroll()
636          .each((obj: RepeatItem<number>) => {
637            ListItem() {
638              Column() {
639                ReusableV2Component({ num: obj.item })
640              }
641            }
642          })
643      }.height('50%')
644      .cachedCount(2)
645    }
646  }
647}
648@ReusableV2
649@ComponentV2
650struct ReusableV2Component {
651  @Require @Param num: number;
652  aboutToAppear() {
653    console.log('ReusableV2Component aboutToAppear');
654  }
655  aboutToRecycle() {
656    console.log('ReusableV2Component aboutToRecycle');
657  }
658  aboutToReuse() {
659    console.log('ReusableV2Component aboutToReuse');
660  }
661  build() {
662    Column() {
663      Text(`${this.num}`).fontSize(50)
664    }
665  }
666}
667```
668
669### Using in ForEach
670>**NOTE**
671>
672>You are advised to use the non-virtualScroll scenarios of **Repeat** to replace **ForEach**.
673
674In the following example, ForEach is used to render multiple reusable components. Each time the **Click to change** button is clicked, the key value changes. Therefore, recycling and reuse are triggered from the second click. (When ForEach determines whether there is reusable nodes, the reuse pool is not initialized. Therefore, when ForEach is clicked for the first time, a new node is created. Then, the reuse pool is initialized from the second click and the node is recycled at the same time.)
675
676```ts
677@Entry
678@ComponentV2
679struct Index {
680  @Local simpleList: number[] = [0, 1, 2, 3, 4, 5];
681  build() {
682    Column() {
683      ForEach(this.simpleList, (num: number, index) => {
684        Row() {
685          Button('Click to change').onClick (()=>{this.simpleList[index]++;})
686          ReusableV2Component({ num: num })
687        }
688      }) // The key changes from each click.
689    }
690  }
691}
692@ReusableV2
693@ComponentV2
694struct ReusableV2Component {
695  @Require @Param num: number;
696  aboutToAppear() {
697    console.log('ReusableV2Component aboutToAppear', this.num); // Triggered when the component is created.
698  }
699  aboutToRecycle() {
700    console.log('ReusableV2Component aboutToRecycle', this.num); // Triggered when the component is recycled.
701  }
702  aboutToReuse() {
703    console.log('ReusableV2Component aboutToReuse', this.num); // Triggered when the component is reused.
704  }
705  build() {
706    Column() {
707      Text(`child: ${this.num}`)
708    }
709  }
710}
711```
712
713
714### Using in LazyForEach
715>**NOTE**
716>
717>You are advised to use the virtualScroll scenarios of **Repeat** to replace **LazyForEach**.
718
719In the following example, **LazyForEach** is used to render several reusable components. During sliding, component creation can be observed first. After all pre-loaded nodes are created, sliding triggers reuse and recycling.
720
721```ts
722class BasicDataSource implements IDataSource {
723  private listeners: DataChangeListener[] = [];
724  private originDataArray: StringData[] = [];
725
726  public totalCount(): number {
727    return 0;
728  }
729
730  public getData(index: number): StringData {
731    return this.originDataArray[index];
732  }
733
734  registerDataChangeListener(listener: DataChangeListener): void {
735    if (this.listeners.indexOf(listener) < 0) {
736      console.info('add listener');
737      this.listeners.push(listener);
738    }
739  }
740
741  unregisterDataChangeListener(listener: DataChangeListener): void {
742    const pos = this.listeners.indexOf(listener);
743    if (pos >= 0) {
744      console.info('remove listener');
745      this.listeners.splice(pos, 1);
746    }
747  }
748
749  notifyDataReload(): void {
750    this.listeners.forEach(listener => {
751      listener.onDataReloaded();
752    })
753  }
754
755  notifyDataAdd(index: number): void {
756    this.listeners.forEach(listener => {
757      listener.onDataAdd(index);
758    })
759  }
760
761  notifyDataChange(index: number): void {
762    this.listeners.forEach(listener => {
763      listener.onDataChange(index);
764    })
765  }
766
767  notifyDataDelete(index: number): void {
768    this.listeners.forEach(listener => {
769      listener.onDataDelete(index);
770    })
771  }
772
773  notifyDataMove(from: number, to: number): void {
774    this.listeners.forEach(listener => {
775      listener.onDataMove(from, to);
776    })
777  }
778
779  notifyDatasetChange(operations: DataOperation[]): void {
780    this.listeners.forEach(listener => {
781      listener.onDatasetChange(operations);
782    })
783  }
784}
785
786class MyDataSource extends BasicDataSource {
787  private dataArray: StringData[] = [];
788
789  public totalCount(): number {
790    return this.dataArray.length;
791  }
792
793  public getData(index: number): StringData {
794    return this.dataArray[index];
795  }
796
797  public addData(index: number, data: StringData): void {
798    this.dataArray.splice(index, 0, data);
799    this.notifyDataAdd(index);
800  }
801
802  public pushData(data: StringData): void {
803    this.dataArray.push(data);
804    this.notifyDataAdd(this.dataArray.length - 1);
805  }
806}
807
808@ObservedV2
809class StringData {
810  @Trace message: string;
811  constructor(message: string) {
812    this.message = message;
813  }
814}
815
816@Entry
817@ComponentV2
818struct Index {
819  data: MyDataSource = new MyDataSource(); // Data source.
820
821  aboutToAppear() {
822    for (let i = 0; i <= 200; i++) {
823      this.data.pushData(new StringData('Hello' + i));
824    }
825  }
826  build() {
827    List({ space: 3 }) {
828      LazyForEach(this.data, (item: StringData, index: number) => {
829        ListItem() {
830          Column() {
831            Text(item.message)
832            ChildComponent({ data: item.message })
833              .onClick(() => {
834                item.message += '!'; // message is a variable decorated by @Trace and its change is observable.
835              })
836          }
837        }
838      })
839    }.cachedCount(5)
840  }
841}
842
843@ReusableV2
844@ComponentV2
845struct ChildComponent {
846  @Param @Require data: string;
847  aboutToAppear(): void {
848    console.log('ChildComponent aboutToAppear', this.data)
849  }
850  aboutToDisappear(): void {
851    console.log('ChildComponent aboutToDisappear', this.data)
852  }
853  aboutToReuse(): void {
854    console.log('ChildComponent aboutToReuse', this.data) // Triggered when the component is reused.
855  }
856  aboutToRecycle(): void {
857    console.log('ChildComponent aboutToRecycle', this.data) // Triggered when the component is recycled.
858  }
859  build() {
860    Row() {
861      Text(this.data).fontSize(50)
862    }
863  }
864}
865```
866