1# Best Practices for Component Reuse
2
3Component reuse is a useful tool in the following scenarios for removing the frame rate bottleneck in the UI thread:
4
51. Scrolling scenarios where instances of the same type of custom components are frequently created and destroyed
62. Scenarios where branches of conditional rendering are switched repeatedly, and the component subtree structure in the branches is complex
7
8For component reuse to take effect, the following conditions must be met:
9
101. The custom component to reuse is decorated by the @Reusable decorator.
112. The reusable custom component (child) created under a custom component (parent) is added to the reuse cache of the parent after being is removed from the component tree.
123. In attempts to create a reusable child component under a custom component (parent), the child is quickly created by updating the reusable component instance of the corresponding type, if any, in the reuse cache of the parent.
13
14## Constraints
15
161. @Reusable indicates that a custom component can be reused. While it can be used to decorate any custom component, take notes of the creation and update processes of the custom component to ensure that the component behaves correctly after being reused.
172. The cache and reuse of reusable custom components can occur only under the same parent component. This means that instances of the same custom component cannot be reused under different parent components. For example, component A is a reusable component, which is also a child component of component B and in the reuse cache of component B. When component A is created in component C, component A cached in component B cannot be used in component C.
183. To reuse a subtree, you only need to decorate its root node with @Reusable. For example, if custom component A has custom child component B, then to reuse the subtree of A and B, you only need to add the @Reusable decorator to A.
194. When a custom component is nested within a reusable custom component, to update the content of the nested component, you must implement the **aboutToReuse** lifecycle callback in the component.
205. The performance benefits of component reuse mainly result from its reducing the time for creating JS objects and reusing the component tree structure. As such, if the component tree structure of custom components is significantly changed due to conditional rendering before or after the reuse, you will not be able to reap the performance benefits of component reuse.
216. Component reuse occurs only when a reusable component is removed from the component tree and then added to the component tree again. For example, if **ForEach** is used to create a reusable custom component, component reuse cannot be triggered due to the full expansion attribute of **ForEach**.
227. Reusable components cannot be nested. That is, if a reusable component exists in the subtree of another reusable component, undefined results may occur.
23
24## Developer's Tips
25
261. To maximize the component reuse performance, avoid any operations that may change the component tree structure or re-lay out the reusable components.
272. For best possible performance, combine component reuse with the **LazyForEach** syntax in list scrolling scenarios.
283. Pay attention to the behavior differences between the creation and update of custom components. Component reuse is, in effect, a special form of component update. The process and lifecycle callbacks used in component creation will not occur during component reuse, and the constructor parameters of the custom component are passed to it through the **aboutToReuse** lifecycle callback. In other words, the **aboutToAppear** lifecycle and initialization parameter input of the custom component will not occur during component reuse.
294. Avoid time-consuming operations during the **aboutToReuse** lifecycle callback. The best practice is to, in **aboutToReuse**, only update the state variable values required for updating custom components.
305. You do not need to update the state variables decorated by @Link, @StorageLink, @ObjectLink, and @Consume in **aboutToReuse**. These state variables are automatically updated, and manual update may trigger unnecessary component re-rendering.
31
32## Lifecycle
33
34When a reusable component is removed from the component tree in C++ code, the **CustomNode** instance of the component on the native side of the ArkUI framework is mounted to the corresponding JSView. When reuse occurs, **CustomNode** is referenced by JSView and the **aboutToRecycle** callback on **ViewPU** is triggered. The **ViewPU** instance is referenced by **RecycleManager**.
35
36When the reusable component is re-added to the component tree from **RecycleManager**, the **aboutToReuse** callback on the frontend **ViewPU** instance is called.
37
38## Available APIs
39
40Called when this reusable component is about to be added from the reuse cache to the component tree. The component's state variables can be updated in this callback. The argument type is the same as the constructor parameter type of the custom component.
41
42```ts
43aboutToReuse?(params: { [key: string]: unknown }): void;
44```
45
46Called when this reusable component is about to be added from the component tree to the reuse cache.
47
48```ts
49aboutToRecycle?(): void;
50```
51
52Adds this reusable component to a reuse group. Components with the same **reuseId** value are reused in the same reuse group.
53
54```ts
55reuseId(id: string);
56```
57
58Declares that a component is reusable.
59
60```ts
61declare const Reusable: ClassDecorator;
62```
63
64**Example**
65
66```ts
67private dataArray: string[] = [];
68  private listener: DataChangeListener;
69
70  public totalCount(): number {
71    return this.dataArray.length;
72  }
73
74  public getData(index: number): any {
75    return this.dataArray[index];
76  }
77
78  public pushData(data: string): void {
79    this.dataArray.push(data);
80  }
81
82  public reloadListener(): void {
83    this.listener.onDataReloaded();
84  }
85
86  public registerDataChangeListener(listener: DataChangeListener): void {
87    this.listener = listener;
88  }
89
90  public unregisterDataChangeListener(listener: DataChangeListener): void {
91    this.listener = null;
92  }
93}
94
95@Entry
96@Component
97struct MyComponent {
98  private data: MyDataSource = new MyDataSource();
99
100  aboutToAppear() {
101    for (let i = 0; i < 1000; i++) {
102      this.data.pushData(i.toString())
103    }
104  }
105
106  build() {
107    List({ space: 3 }) {
108      LazyForEach(this.data, (item: string) => {
109        ListItem() {
110          ReusableChildComponent({ item: item })
111        }
112      }, item => item)
113    }
114    .width('100%')
115    .height('100%')
116  }
117}
118
119@Reusable
120@Component
121struct ReusableChildComponent {
122  @State item: string = ''
123
124  aboutToReuse(params) {
125    this.item = params.item;
126  }
127
128  build() {
129    Row() {
130      Text(this.item)
131        .fontSize(20)
132        .margin({ left: 10 })
133    }.margin({ left: 10, right: 10 })
134  }
135}
136```
137
138## Samples
139
140The following sample code from a shopping application exemplifies code before and after component reuse and the benefits that can be reaped from component reuse.
141
142### Code Before and After Component Reuse
143
144**Before Component Reuse**
145
146```ts
147LazyForEach(this.GoodDataOne, (item, index) => {
148  GridItem() {
149    Column() {
150      Image(item.img)
151        .height(item.hei)
152        .width('100%')
153        .objectFit(ImageFit.Fill)
154
155      Text(item.introduce)
156        .fontSize(14)
157        .padding({ left: 5, right: 5 })
158        .margin({ top: 5 })
159      Row() {
160        Row() {
161          Text('¥')
162            .fontSize(10)
163            .fontColor(Color.Red)
164            .baselineOffset(-4)
165          Text(item.price)
166            .fontSize(16)
167            .fontColor(Color.Red)
168          Text(item.numb)
169            .fontSize(10)
170            .fontColor(Color.Gray)
171            .baselineOffset(-4)
172            .margin({ left: 5 })
173        }
174
175        Image($r('app.media.photo63'))
176          .width(20)
177          .height(10)
178          .margin({ bottom: -8 })
179      }
180      .width('100%')
181      .justifyContent(FlexAlign.SpaceBetween)
182      .padding({ left: 5, right: 5 })
183      .margin({ top: 15 })
184    }
185    .borderRadius(10)
186    .backgroundColor(Color.White)
187    .clip(true)
188    .width('100%')
189    .height(290)
190  }
191}, (item) => JSON.stringify(item))
192```
193
194**After Component Reuse**
195
196When the component is reused, the ArkUI framework passes to the **aboutToReuse** lifecycle callback the constructor parameters of the component. Assign values to the state variables to be updated in **aboutToReuse**, and the ArkUI framework will display the UI with the latest state variable values.
197
198If the instances of a custom component vary greatly, use **reuseId** to assign these instances to different reuse groups to achieve the optimal effect.
199
200If a custom component has a reference to a large object or other unnecessary resources, release the reference in the **aboutToRecycle** lifecycle callback to avoid memory leak.
201
202```ts
203LazyForEach(this.GoodDataOne, (item, index) => {
204  GridItem() {
205    GoodItems({
206      boo:item.data.boo,
207      img:item.data.img,
208      webimg:item.data.webimg,
209      hei:item.data.hei,
210      introduce:item.data.introduce,
211      price:item.data.price,
212      numb:item.data.numb,
213      index:index
214    })
215    .reuseId(this.CombineStr(item.type))
216  }
217}, (item) => JSON.stringify(item))
218
219
220@Reusable
221@Component
222struct GoodItems {
223  @State img: Resource = $r("app.media.photo61")
224  @State webimg?: string = ''
225  @State hei: number = 0
226  @State introduce: string = ''
227  @State price: string = ''
228  @State numb: string = ''
229  @LocalStorageLink('storageSimpleProp') simpleVarName: string = ''
230  boo: boolean = true
231  index: number = 0
232  controllerVideo: VideoController = new VideoController();
233
234  aboutToReuse(params)
235  {
236    this.webimg = params.webimg
237    this.img = params.img
238    this.hei = params.hei
239    this.introduce = params.introduce
240    this.price = params.price
241    this.numb = params.numb
242  }
243
244  build() {
245    // ...
246  }
247}
248```
249
250### Performance Benefit
251
252Analysis results from the profiler tool in DevEco Studio show that, with component reuse, the average component creation time is reduced from 1800 μs to 570 μs.
253
254![before recycle](./figures/before-recycle.png)
255
256![using recycle](./figures/using-recycle.png)
257
258| Component Reuse | Component Creation Time|
259| -------------- | ------------ |
260| Component reuse disabled| 1813 μs      |
261| Component reuse enabled  | 570 μs       |
262