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 255 256 257 258| Component Reuse | Component Creation Time| 259| -------------- | ------------ | 260| Component reuse disabled| 1813 μs | 261| Component reuse enabled | 570 μs | 262