1e41f4b71Sopenharmony_ci# Best Practices for Component Reuse 2e41f4b71Sopenharmony_ci 3e41f4b71Sopenharmony_ciComponent reuse is a useful tool in the following scenarios for removing the frame rate bottleneck in the UI thread: 4e41f4b71Sopenharmony_ci 5e41f4b71Sopenharmony_ci1. Scrolling scenarios where instances of the same type of custom components are frequently created and destroyed 6e41f4b71Sopenharmony_ci2. Scenarios where branches of conditional rendering are switched repeatedly, and the component subtree structure in the branches is complex 7e41f4b71Sopenharmony_ci 8e41f4b71Sopenharmony_ciFor component reuse to take effect, the following conditions must be met: 9e41f4b71Sopenharmony_ci 10e41f4b71Sopenharmony_ci1. The custom component to reuse is decorated by the @Reusable decorator. 11e41f4b71Sopenharmony_ci2. 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. 12e41f4b71Sopenharmony_ci3. 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. 13e41f4b71Sopenharmony_ci 14e41f4b71Sopenharmony_ci## Constraints 15e41f4b71Sopenharmony_ci 16e41f4b71Sopenharmony_ci1. @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. 17e41f4b71Sopenharmony_ci2. 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. 18e41f4b71Sopenharmony_ci3. 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. 19e41f4b71Sopenharmony_ci4. 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. 20e41f4b71Sopenharmony_ci5. 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. 21e41f4b71Sopenharmony_ci6. 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**. 22e41f4b71Sopenharmony_ci7. Reusable components cannot be nested. That is, if a reusable component exists in the subtree of another reusable component, undefined results may occur. 23e41f4b71Sopenharmony_ci 24e41f4b71Sopenharmony_ci## Developer's Tips 25e41f4b71Sopenharmony_ci 26e41f4b71Sopenharmony_ci1. To maximize the component reuse performance, avoid any operations that may change the component tree structure or re-lay out the reusable components. 27e41f4b71Sopenharmony_ci2. For best possible performance, combine component reuse with the **LazyForEach** syntax in list scrolling scenarios. 28e41f4b71Sopenharmony_ci3. 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. 29e41f4b71Sopenharmony_ci4. 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. 30e41f4b71Sopenharmony_ci5. 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. 31e41f4b71Sopenharmony_ci 32e41f4b71Sopenharmony_ci## Lifecycle 33e41f4b71Sopenharmony_ci 34e41f4b71Sopenharmony_ciWhen 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**. 35e41f4b71Sopenharmony_ci 36e41f4b71Sopenharmony_ciWhen the reusable component is re-added to the component tree from **RecycleManager**, the **aboutToReuse** callback on the frontend **ViewPU** instance is called. 37e41f4b71Sopenharmony_ci 38e41f4b71Sopenharmony_ci## Available APIs 39e41f4b71Sopenharmony_ci 40e41f4b71Sopenharmony_ciCalled 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. 41e41f4b71Sopenharmony_ci 42e41f4b71Sopenharmony_ci```ts 43e41f4b71Sopenharmony_ciaboutToReuse?(params: { [key: string]: unknown }): void; 44e41f4b71Sopenharmony_ci``` 45e41f4b71Sopenharmony_ci 46e41f4b71Sopenharmony_ciCalled when this reusable component is about to be added from the component tree to the reuse cache. 47e41f4b71Sopenharmony_ci 48e41f4b71Sopenharmony_ci```ts 49e41f4b71Sopenharmony_ciaboutToRecycle?(): void; 50e41f4b71Sopenharmony_ci``` 51e41f4b71Sopenharmony_ci 52e41f4b71Sopenharmony_ciAdds this reusable component to a reuse group. Components with the same **reuseId** value are reused in the same reuse group. 53e41f4b71Sopenharmony_ci 54e41f4b71Sopenharmony_ci```ts 55e41f4b71Sopenharmony_cireuseId(id: string); 56e41f4b71Sopenharmony_ci``` 57e41f4b71Sopenharmony_ci 58e41f4b71Sopenharmony_ciDeclares that a component is reusable. 59e41f4b71Sopenharmony_ci 60e41f4b71Sopenharmony_ci```ts 61e41f4b71Sopenharmony_cideclare const Reusable: ClassDecorator; 62e41f4b71Sopenharmony_ci``` 63e41f4b71Sopenharmony_ci 64e41f4b71Sopenharmony_ci**Example** 65e41f4b71Sopenharmony_ci 66e41f4b71Sopenharmony_ci```ts 67e41f4b71Sopenharmony_ciprivate dataArray: string[] = []; 68e41f4b71Sopenharmony_ci private listener: DataChangeListener; 69e41f4b71Sopenharmony_ci 70e41f4b71Sopenharmony_ci public totalCount(): number { 71e41f4b71Sopenharmony_ci return this.dataArray.length; 72e41f4b71Sopenharmony_ci } 73e41f4b71Sopenharmony_ci 74e41f4b71Sopenharmony_ci public getData(index: number): any { 75e41f4b71Sopenharmony_ci return this.dataArray[index]; 76e41f4b71Sopenharmony_ci } 77e41f4b71Sopenharmony_ci 78e41f4b71Sopenharmony_ci public pushData(data: string): void { 79e41f4b71Sopenharmony_ci this.dataArray.push(data); 80e41f4b71Sopenharmony_ci } 81e41f4b71Sopenharmony_ci 82e41f4b71Sopenharmony_ci public reloadListener(): void { 83e41f4b71Sopenharmony_ci this.listener.onDataReloaded(); 84e41f4b71Sopenharmony_ci } 85e41f4b71Sopenharmony_ci 86e41f4b71Sopenharmony_ci public registerDataChangeListener(listener: DataChangeListener): void { 87e41f4b71Sopenharmony_ci this.listener = listener; 88e41f4b71Sopenharmony_ci } 89e41f4b71Sopenharmony_ci 90e41f4b71Sopenharmony_ci public unregisterDataChangeListener(listener: DataChangeListener): void { 91e41f4b71Sopenharmony_ci this.listener = null; 92e41f4b71Sopenharmony_ci } 93e41f4b71Sopenharmony_ci} 94e41f4b71Sopenharmony_ci 95e41f4b71Sopenharmony_ci@Entry 96e41f4b71Sopenharmony_ci@Component 97e41f4b71Sopenharmony_cistruct MyComponent { 98e41f4b71Sopenharmony_ci private data: MyDataSource = new MyDataSource(); 99e41f4b71Sopenharmony_ci 100e41f4b71Sopenharmony_ci aboutToAppear() { 101e41f4b71Sopenharmony_ci for (let i = 0; i < 1000; i++) { 102e41f4b71Sopenharmony_ci this.data.pushData(i.toString()) 103e41f4b71Sopenharmony_ci } 104e41f4b71Sopenharmony_ci } 105e41f4b71Sopenharmony_ci 106e41f4b71Sopenharmony_ci build() { 107e41f4b71Sopenharmony_ci List({ space: 3 }) { 108e41f4b71Sopenharmony_ci LazyForEach(this.data, (item: string) => { 109e41f4b71Sopenharmony_ci ListItem() { 110e41f4b71Sopenharmony_ci ReusableChildComponent({ item: item }) 111e41f4b71Sopenharmony_ci } 112e41f4b71Sopenharmony_ci }, item => item) 113e41f4b71Sopenharmony_ci } 114e41f4b71Sopenharmony_ci .width('100%') 115e41f4b71Sopenharmony_ci .height('100%') 116e41f4b71Sopenharmony_ci } 117e41f4b71Sopenharmony_ci} 118e41f4b71Sopenharmony_ci 119e41f4b71Sopenharmony_ci@Reusable 120e41f4b71Sopenharmony_ci@Component 121e41f4b71Sopenharmony_cistruct ReusableChildComponent { 122e41f4b71Sopenharmony_ci @State item: string = '' 123e41f4b71Sopenharmony_ci 124e41f4b71Sopenharmony_ci aboutToReuse(params) { 125e41f4b71Sopenharmony_ci this.item = params.item; 126e41f4b71Sopenharmony_ci } 127e41f4b71Sopenharmony_ci 128e41f4b71Sopenharmony_ci build() { 129e41f4b71Sopenharmony_ci Row() { 130e41f4b71Sopenharmony_ci Text(this.item) 131e41f4b71Sopenharmony_ci .fontSize(20) 132e41f4b71Sopenharmony_ci .margin({ left: 10 }) 133e41f4b71Sopenharmony_ci }.margin({ left: 10, right: 10 }) 134e41f4b71Sopenharmony_ci } 135e41f4b71Sopenharmony_ci} 136e41f4b71Sopenharmony_ci``` 137e41f4b71Sopenharmony_ci 138e41f4b71Sopenharmony_ci## Samples 139e41f4b71Sopenharmony_ci 140e41f4b71Sopenharmony_ciThe following sample code from a shopping application exemplifies code before and after component reuse and the benefits that can be reaped from component reuse. 141e41f4b71Sopenharmony_ci 142e41f4b71Sopenharmony_ci### Code Before and After Component Reuse 143e41f4b71Sopenharmony_ci 144e41f4b71Sopenharmony_ci**Before Component Reuse** 145e41f4b71Sopenharmony_ci 146e41f4b71Sopenharmony_ci```ts 147e41f4b71Sopenharmony_ciLazyForEach(this.GoodDataOne, (item, index) => { 148e41f4b71Sopenharmony_ci GridItem() { 149e41f4b71Sopenharmony_ci Column() { 150e41f4b71Sopenharmony_ci Image(item.img) 151e41f4b71Sopenharmony_ci .height(item.hei) 152e41f4b71Sopenharmony_ci .width('100%') 153e41f4b71Sopenharmony_ci .objectFit(ImageFit.Fill) 154e41f4b71Sopenharmony_ci 155e41f4b71Sopenharmony_ci Text(item.introduce) 156e41f4b71Sopenharmony_ci .fontSize(14) 157e41f4b71Sopenharmony_ci .padding({ left: 5, right: 5 }) 158e41f4b71Sopenharmony_ci .margin({ top: 5 }) 159e41f4b71Sopenharmony_ci Row() { 160e41f4b71Sopenharmony_ci Row() { 161e41f4b71Sopenharmony_ci Text('¥') 162e41f4b71Sopenharmony_ci .fontSize(10) 163e41f4b71Sopenharmony_ci .fontColor(Color.Red) 164e41f4b71Sopenharmony_ci .baselineOffset(-4) 165e41f4b71Sopenharmony_ci Text(item.price) 166e41f4b71Sopenharmony_ci .fontSize(16) 167e41f4b71Sopenharmony_ci .fontColor(Color.Red) 168e41f4b71Sopenharmony_ci Text(item.numb) 169e41f4b71Sopenharmony_ci .fontSize(10) 170e41f4b71Sopenharmony_ci .fontColor(Color.Gray) 171e41f4b71Sopenharmony_ci .baselineOffset(-4) 172e41f4b71Sopenharmony_ci .margin({ left: 5 }) 173e41f4b71Sopenharmony_ci } 174e41f4b71Sopenharmony_ci 175e41f4b71Sopenharmony_ci Image($r('app.media.photo63')) 176e41f4b71Sopenharmony_ci .width(20) 177e41f4b71Sopenharmony_ci .height(10) 178e41f4b71Sopenharmony_ci .margin({ bottom: -8 }) 179e41f4b71Sopenharmony_ci } 180e41f4b71Sopenharmony_ci .width('100%') 181e41f4b71Sopenharmony_ci .justifyContent(FlexAlign.SpaceBetween) 182e41f4b71Sopenharmony_ci .padding({ left: 5, right: 5 }) 183e41f4b71Sopenharmony_ci .margin({ top: 15 }) 184e41f4b71Sopenharmony_ci } 185e41f4b71Sopenharmony_ci .borderRadius(10) 186e41f4b71Sopenharmony_ci .backgroundColor(Color.White) 187e41f4b71Sopenharmony_ci .clip(true) 188e41f4b71Sopenharmony_ci .width('100%') 189e41f4b71Sopenharmony_ci .height(290) 190e41f4b71Sopenharmony_ci } 191e41f4b71Sopenharmony_ci}, (item) => JSON.stringify(item)) 192e41f4b71Sopenharmony_ci``` 193e41f4b71Sopenharmony_ci 194e41f4b71Sopenharmony_ci**After Component Reuse** 195e41f4b71Sopenharmony_ci 196e41f4b71Sopenharmony_ciWhen 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. 197e41f4b71Sopenharmony_ci 198e41f4b71Sopenharmony_ciIf the instances of a custom component vary greatly, use **reuseId** to assign these instances to different reuse groups to achieve the optimal effect. 199e41f4b71Sopenharmony_ci 200e41f4b71Sopenharmony_ciIf 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. 201e41f4b71Sopenharmony_ci 202e41f4b71Sopenharmony_ci```ts 203e41f4b71Sopenharmony_ciLazyForEach(this.GoodDataOne, (item, index) => { 204e41f4b71Sopenharmony_ci GridItem() { 205e41f4b71Sopenharmony_ci GoodItems({ 206e41f4b71Sopenharmony_ci boo:item.data.boo, 207e41f4b71Sopenharmony_ci img:item.data.img, 208e41f4b71Sopenharmony_ci webimg:item.data.webimg, 209e41f4b71Sopenharmony_ci hei:item.data.hei, 210e41f4b71Sopenharmony_ci introduce:item.data.introduce, 211e41f4b71Sopenharmony_ci price:item.data.price, 212e41f4b71Sopenharmony_ci numb:item.data.numb, 213e41f4b71Sopenharmony_ci index:index 214e41f4b71Sopenharmony_ci }) 215e41f4b71Sopenharmony_ci .reuseId(this.CombineStr(item.type)) 216e41f4b71Sopenharmony_ci } 217e41f4b71Sopenharmony_ci}, (item) => JSON.stringify(item)) 218e41f4b71Sopenharmony_ci 219e41f4b71Sopenharmony_ci 220e41f4b71Sopenharmony_ci@Reusable 221e41f4b71Sopenharmony_ci@Component 222e41f4b71Sopenharmony_cistruct GoodItems { 223e41f4b71Sopenharmony_ci @State img: Resource = $r("app.media.photo61") 224e41f4b71Sopenharmony_ci @State webimg?: string = '' 225e41f4b71Sopenharmony_ci @State hei: number = 0 226e41f4b71Sopenharmony_ci @State introduce: string = '' 227e41f4b71Sopenharmony_ci @State price: string = '' 228e41f4b71Sopenharmony_ci @State numb: string = '' 229e41f4b71Sopenharmony_ci @LocalStorageLink('storageSimpleProp') simpleVarName: string = '' 230e41f4b71Sopenharmony_ci boo: boolean = true 231e41f4b71Sopenharmony_ci index: number = 0 232e41f4b71Sopenharmony_ci controllerVideo: VideoController = new VideoController(); 233e41f4b71Sopenharmony_ci 234e41f4b71Sopenharmony_ci aboutToReuse(params) 235e41f4b71Sopenharmony_ci { 236e41f4b71Sopenharmony_ci this.webimg = params.webimg 237e41f4b71Sopenharmony_ci this.img = params.img 238e41f4b71Sopenharmony_ci this.hei = params.hei 239e41f4b71Sopenharmony_ci this.introduce = params.introduce 240e41f4b71Sopenharmony_ci this.price = params.price 241e41f4b71Sopenharmony_ci this.numb = params.numb 242e41f4b71Sopenharmony_ci } 243e41f4b71Sopenharmony_ci 244e41f4b71Sopenharmony_ci build() { 245e41f4b71Sopenharmony_ci // ... 246e41f4b71Sopenharmony_ci } 247e41f4b71Sopenharmony_ci} 248e41f4b71Sopenharmony_ci``` 249e41f4b71Sopenharmony_ci 250e41f4b71Sopenharmony_ci### Performance Benefit 251e41f4b71Sopenharmony_ci 252e41f4b71Sopenharmony_ciAnalysis 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. 253e41f4b71Sopenharmony_ci 254e41f4b71Sopenharmony_ci 255e41f4b71Sopenharmony_ci 256e41f4b71Sopenharmony_ci 257e41f4b71Sopenharmony_ci 258e41f4b71Sopenharmony_ci| Component Reuse | Component Creation Time| 259e41f4b71Sopenharmony_ci| -------------- | ------------ | 260e41f4b71Sopenharmony_ci| Component reuse disabled| 1813 μs | 261e41f4b71Sopenharmony_ci| Component reuse enabled | 570 μs | 262