1e41f4b71Sopenharmony_ci# Proper Use of State Management 2e41f4b71Sopenharmony_ci 3e41f4b71Sopenharmony_ciManaging state in applications can be a tricky task. You may find the UI not refreshed as expected, or the re-renders slowing down your application. This topic explores some best practices for managing state, through typical correct and incorrect usage examples. 4e41f4b71Sopenharmony_ci 5e41f4b71Sopenharmony_ci## Properly Using Attributes 6e41f4b71Sopenharmony_ci 7e41f4b71Sopenharmony_ci### Combining Simple Attributes into Object Arrays 8e41f4b71Sopenharmony_ci 9e41f4b71Sopenharmony_ciIt is commonplace in development to set the same attribute for multiple components, for example, the text content, width, or height attributes. To make these attributes easier to manage, you can store them in an array and use them with **ForEach**. 10e41f4b71Sopenharmony_ci 11e41f4b71Sopenharmony_ci```typescript 12e41f4b71Sopenharmony_ci@Entry 13e41f4b71Sopenharmony_ci@Component 14e41f4b71Sopenharmony_cistruct Index { 15e41f4b71Sopenharmony_ci @State items: string[] = []; 16e41f4b71Sopenharmony_ci @State ids: string[] = []; 17e41f4b71Sopenharmony_ci @State age: number[] = []; 18e41f4b71Sopenharmony_ci @State gender: string[] = []; 19e41f4b71Sopenharmony_ci 20e41f4b71Sopenharmony_ci aboutToAppear() { 21e41f4b71Sopenharmony_ci this.items.push("Head"); 22e41f4b71Sopenharmony_ci this.items.push("List"); 23e41f4b71Sopenharmony_ci for (let i = 0; i < 20; i++) { 24e41f4b71Sopenharmony_ci this.ids.push("id: " + Math.floor(Math.random() * 1000)); 25e41f4b71Sopenharmony_ci this.age.push(Math.floor(Math.random() * 100 % 40)); 26e41f4b71Sopenharmony_ci this.gender.push(Math.floor(Math.random() * 100) % 2 == 0 ? "Male" : "Female"); 27e41f4b71Sopenharmony_ci } 28e41f4b71Sopenharmony_ci } 29e41f4b71Sopenharmony_ci 30e41f4b71Sopenharmony_ci isRenderText(index: number) : number { 31e41f4b71Sopenharmony_ci console.log(`index ${index} is rendered`); 32e41f4b71Sopenharmony_ci return 1; 33e41f4b71Sopenharmony_ci } 34e41f4b71Sopenharmony_ci 35e41f4b71Sopenharmony_ci build() { 36e41f4b71Sopenharmony_ci Row() { 37e41f4b71Sopenharmony_ci Column() { 38e41f4b71Sopenharmony_ci ForEach(this.items, (item: string) => { 39e41f4b71Sopenharmony_ci if (item == "Head") { 40e41f4b71Sopenharmony_ci Text("Personal Info") 41e41f4b71Sopenharmony_ci .fontSize(40) 42e41f4b71Sopenharmony_ci } else if (item == "List") { 43e41f4b71Sopenharmony_ci List() { 44e41f4b71Sopenharmony_ci ForEach(this.ids, (id: string, index) => { 45e41f4b71Sopenharmony_ci ListItem() { 46e41f4b71Sopenharmony_ci Row() { 47e41f4b71Sopenharmony_ci Text(id) 48e41f4b71Sopenharmony_ci .fontSize(20) 49e41f4b71Sopenharmony_ci .margin({ 50e41f4b71Sopenharmony_ci left: 30, 51e41f4b71Sopenharmony_ci right: 5 52e41f4b71Sopenharmony_ci }) 53e41f4b71Sopenharmony_ci Text("age: " + this.age[index as number]) 54e41f4b71Sopenharmony_ci .fontSize(20) 55e41f4b71Sopenharmony_ci .margin({ 56e41f4b71Sopenharmony_ci left: 5, 57e41f4b71Sopenharmony_ci right: 5 58e41f4b71Sopenharmony_ci }) 59e41f4b71Sopenharmony_ci .position({x: 100}) 60e41f4b71Sopenharmony_ci .opacity(this.isRenderText(index)) 61e41f4b71Sopenharmony_ci .onClick(() => { 62e41f4b71Sopenharmony_ci this.age[index]++; 63e41f4b71Sopenharmony_ci }) 64e41f4b71Sopenharmony_ci Text("gender: " + this.gender[index as number]) 65e41f4b71Sopenharmony_ci .margin({ 66e41f4b71Sopenharmony_ci left: 5, 67e41f4b71Sopenharmony_ci right: 5 68e41f4b71Sopenharmony_ci }) 69e41f4b71Sopenharmony_ci .position({x: 180}) 70e41f4b71Sopenharmony_ci .fontSize(20) 71e41f4b71Sopenharmony_ci } 72e41f4b71Sopenharmony_ci } 73e41f4b71Sopenharmony_ci .margin({ 74e41f4b71Sopenharmony_ci top: 5, 75e41f4b71Sopenharmony_ci bottom: 5 76e41f4b71Sopenharmony_ci }) 77e41f4b71Sopenharmony_ci }) 78e41f4b71Sopenharmony_ci } 79e41f4b71Sopenharmony_ci } 80e41f4b71Sopenharmony_ci }) 81e41f4b71Sopenharmony_ci } 82e41f4b71Sopenharmony_ci } 83e41f4b71Sopenharmony_ci } 84e41f4b71Sopenharmony_ci} 85e41f4b71Sopenharmony_ci``` 86e41f4b71Sopenharmony_ci 87e41f4b71Sopenharmony_ciBelow you can see how the preceding code snippet works. 88e41f4b71Sopenharmony_ci 89e41f4b71Sopenharmony_ci 90e41f4b71Sopenharmony_ci 91e41f4b71Sopenharmony_ciIn this example, a total of 20 records are displayed on the page through **ForEach**. When you click the **Text** component of **age** in one of the records, the **Text** components of **age** in other 19 records are also re-rendered - reflected by the logs generated for the components of **age**. However, because the **age** values of the other 19 records do not change, the re-rendering of these records is actually redundant. 92e41f4b71Sopenharmony_ci 93e41f4b71Sopenharmony_ciThis redundant re-rendering is due to a characteristic of state management. Assume that there is an @State decorated number array **Num[]**. This array contains 20 elements whose values are 0 to 19, respectively. Each of the 20 elements is bound to a **Text** component. When one of the elements is changed, all components bound to the elements are re-rendered, regardless of whether the other elements are changed or not. 94e41f4b71Sopenharmony_ci 95e41f4b71Sopenharmony_ciThis seemly bug, commonly known as "redundant re-render", is widely observed in simple array, and can adversely affect the UI re-rendering performance when the arrays are large. To make your rendering process run smoothly, it is crucial to reduce redundant re-renders and update components only when necessary. 96e41f4b71Sopenharmony_ci 97e41f4b71Sopenharmony_ciIn the case of an array of simple attributes, you can avoid redundant re-rendering by converting the array into an object array. The code snippet after optimization is as follows: 98e41f4b71Sopenharmony_ci 99e41f4b71Sopenharmony_ci```typescript 100e41f4b71Sopenharmony_ci@Observed 101e41f4b71Sopenharmony_ciclass InfoList extends Array<Info> { 102e41f4b71Sopenharmony_ci}; 103e41f4b71Sopenharmony_ci@Observed 104e41f4b71Sopenharmony_ciclass Info { 105e41f4b71Sopenharmony_ci ids: number; 106e41f4b71Sopenharmony_ci age: number; 107e41f4b71Sopenharmony_ci gender: string; 108e41f4b71Sopenharmony_ci 109e41f4b71Sopenharmony_ci constructor() { 110e41f4b71Sopenharmony_ci this.ids = Math.floor(Math.random() * 1000); 111e41f4b71Sopenharmony_ci this.age = Math.floor(Math.random() * 100 % 40); 112e41f4b71Sopenharmony_ci this.gender = Math.floor(Math.random() * 100) % 2 == 0 ? "Male" : "Female"; 113e41f4b71Sopenharmony_ci } 114e41f4b71Sopenharmony_ci} 115e41f4b71Sopenharmony_ci@Component 116e41f4b71Sopenharmony_cistruct Information { 117e41f4b71Sopenharmony_ci @ObjectLink info: Info; 118e41f4b71Sopenharmony_ci @State index: number = 0; 119e41f4b71Sopenharmony_ci isRenderText(index: number) : number { 120e41f4b71Sopenharmony_ci console.log(`index ${index} is rendered`); 121e41f4b71Sopenharmony_ci return 1; 122e41f4b71Sopenharmony_ci } 123e41f4b71Sopenharmony_ci 124e41f4b71Sopenharmony_ci build() { 125e41f4b71Sopenharmony_ci Row() { 126e41f4b71Sopenharmony_ci Text("id: " + this.info.ids) 127e41f4b71Sopenharmony_ci .fontSize(20) 128e41f4b71Sopenharmony_ci .margin({ 129e41f4b71Sopenharmony_ci left: 30, 130e41f4b71Sopenharmony_ci right: 5 131e41f4b71Sopenharmony_ci }) 132e41f4b71Sopenharmony_ci Text("age: " + this.info.age) 133e41f4b71Sopenharmony_ci .fontSize(20) 134e41f4b71Sopenharmony_ci .margin({ 135e41f4b71Sopenharmony_ci left: 5, 136e41f4b71Sopenharmony_ci right: 5 137e41f4b71Sopenharmony_ci }) 138e41f4b71Sopenharmony_ci .position({x: 100}) 139e41f4b71Sopenharmony_ci .opacity(this.isRenderText(this.index)) 140e41f4b71Sopenharmony_ci .onClick(() => { 141e41f4b71Sopenharmony_ci this.info.age++; 142e41f4b71Sopenharmony_ci }) 143e41f4b71Sopenharmony_ci Text("gender: " + this.info.gender) 144e41f4b71Sopenharmony_ci .margin({ 145e41f4b71Sopenharmony_ci left: 5, 146e41f4b71Sopenharmony_ci right: 5 147e41f4b71Sopenharmony_ci }) 148e41f4b71Sopenharmony_ci .position({x: 180}) 149e41f4b71Sopenharmony_ci .fontSize(20) 150e41f4b71Sopenharmony_ci } 151e41f4b71Sopenharmony_ci } 152e41f4b71Sopenharmony_ci} 153e41f4b71Sopenharmony_ci@Entry 154e41f4b71Sopenharmony_ci@Component 155e41f4b71Sopenharmony_cistruct Page { 156e41f4b71Sopenharmony_ci @State infoList: InfoList = new InfoList(); 157e41f4b71Sopenharmony_ci @State items: string[] = []; 158e41f4b71Sopenharmony_ci aboutToAppear() { 159e41f4b71Sopenharmony_ci this.items.push("Head"); 160e41f4b71Sopenharmony_ci this.items.push("List"); 161e41f4b71Sopenharmony_ci for (let i = 0; i < 20; i++) { 162e41f4b71Sopenharmony_ci this.infoList.push(new Info()); 163e41f4b71Sopenharmony_ci } 164e41f4b71Sopenharmony_ci } 165e41f4b71Sopenharmony_ci 166e41f4b71Sopenharmony_ci build() { 167e41f4b71Sopenharmony_ci Row() { 168e41f4b71Sopenharmony_ci Column() { 169e41f4b71Sopenharmony_ci ForEach(this.items, (item: string) => { 170e41f4b71Sopenharmony_ci if (item == "Head") { 171e41f4b71Sopenharmony_ci Text("Personal Info") 172e41f4b71Sopenharmony_ci .fontSize(40) 173e41f4b71Sopenharmony_ci } else if (item == "List") { 174e41f4b71Sopenharmony_ci List() { 175e41f4b71Sopenharmony_ci ForEach(this.infoList, (info: Info, index) => { 176e41f4b71Sopenharmony_ci ListItem() { 177e41f4b71Sopenharmony_ci Information({ 178e41f4b71Sopenharmony_ci // in low version, DevEco may throw a warning, but it does not matter. 179e41f4b71Sopenharmony_ci // you can still compile and run. 180e41f4b71Sopenharmony_ci info: info, 181e41f4b71Sopenharmony_ci index: index 182e41f4b71Sopenharmony_ci }) 183e41f4b71Sopenharmony_ci } 184e41f4b71Sopenharmony_ci .margin({ 185e41f4b71Sopenharmony_ci top: 5, 186e41f4b71Sopenharmony_ci bottom: 5 187e41f4b71Sopenharmony_ci }) 188e41f4b71Sopenharmony_ci }) 189e41f4b71Sopenharmony_ci } 190e41f4b71Sopenharmony_ci } 191e41f4b71Sopenharmony_ci }) 192e41f4b71Sopenharmony_ci } 193e41f4b71Sopenharmony_ci } 194e41f4b71Sopenharmony_ci } 195e41f4b71Sopenharmony_ci} 196e41f4b71Sopenharmony_ci``` 197e41f4b71Sopenharmony_ci 198e41f4b71Sopenharmony_ciBelow you can see how the preceding code snippet works. 199e41f4b71Sopenharmony_ci 200e41f4b71Sopenharmony_ci 201e41f4b71Sopenharmony_ci 202e41f4b71Sopenharmony_ciAfter optimization, an object array is used in place of the original attribute arrays. For an array, changes in an object cannot be observed and therefore do not cause re-renders. Specifically, only changes at the top level of array items can be observed, for example, adding, modifying, or deleting an item. For a common array, modifying a data item means to change the item's value. For an object array, it means to assign a new value to the entire object, which means that changes to a property in an object are not observable to the array and consequently do not cause a re-render. In addition to property changes in object arrays, changes in nested objects cannot be observed either, which is further detailed in [Splitting a Complex Large Object into Multiple Small Objects](#splitting-a-complex-large-object-into-multiple-small-objects). In the code after optimization, you may notice a combination of custom components and **ForEach**. For details, see [Using Custom Components to Match Object Arrays in ForEach](#using-custom-components-to-match-object-arrays-in-foreach). 203e41f4b71Sopenharmony_ci 204e41f4b71Sopenharmony_ci### Splitting a Complex Large Object into Multiple Small Objects 205e41f4b71Sopenharmony_ci 206e41f4b71Sopenharmony_ci> **NOTE** 207e41f4b71Sopenharmony_ci> 208e41f4b71Sopenharmony_ci> You are advised to use the [@Track](arkts-track.md) decorator in this scenario since API version 11. 209e41f4b71Sopenharmony_ci 210e41f4b71Sopenharmony_ciDuring development, we sometimes define a large object that contains many style-related properties, and pass the object between parent and child components to bind the properties to the components. 211e41f4b71Sopenharmony_ci 212e41f4b71Sopenharmony_ci```typescript 213e41f4b71Sopenharmony_ci@Observed 214e41f4b71Sopenharmony_ciclass UIStyle { 215e41f4b71Sopenharmony_ci translateX: number = 0; 216e41f4b71Sopenharmony_ci translateY: number = 0; 217e41f4b71Sopenharmony_ci scaleX: number = 0.3; 218e41f4b71Sopenharmony_ci scaleY: number = 0.3; 219e41f4b71Sopenharmony_ci width: number = 336; 220e41f4b71Sopenharmony_ci height: number = 178; 221e41f4b71Sopenharmony_ci posX: number = 10; 222e41f4b71Sopenharmony_ci posY: number = 50; 223e41f4b71Sopenharmony_ci alpha: number = 0.5; 224e41f4b71Sopenharmony_ci borderRadius: number = 24; 225e41f4b71Sopenharmony_ci imageWidth: number = 78; 226e41f4b71Sopenharmony_ci imageHeight: number = 78; 227e41f4b71Sopenharmony_ci translateImageX: number = 0; 228e41f4b71Sopenharmony_ci translateImageY: number = 0; 229e41f4b71Sopenharmony_ci fontSize: number = 20; 230e41f4b71Sopenharmony_ci} 231e41f4b71Sopenharmony_ci@Component 232e41f4b71Sopenharmony_cistruct SpecialImage { 233e41f4b71Sopenharmony_ci @ObjectLink uiStyle: UIStyle; 234e41f4b71Sopenharmony_ci private isRenderSpecialImage() : number { // function to show whether the component is rendered 235e41f4b71Sopenharmony_ci console.log("SpecialImage is rendered"); 236e41f4b71Sopenharmony_ci return 1; 237e41f4b71Sopenharmony_ci } 238e41f4b71Sopenharmony_ci build() { 239e41f4b71Sopenharmony_ci Image($r('app.media.icon')) // Use app.media.app_icon since API version 12. 240e41f4b71Sopenharmony_ci .width(this.uiStyle.imageWidth) 241e41f4b71Sopenharmony_ci .height(this.uiStyle.imageHeight) 242e41f4b71Sopenharmony_ci .margin({ top: 20 }) 243e41f4b71Sopenharmony_ci .translate({ 244e41f4b71Sopenharmony_ci x: this.uiStyle.translateImageX, 245e41f4b71Sopenharmony_ci y: this.uiStyle.translateImageY 246e41f4b71Sopenharmony_ci }) 247e41f4b71Sopenharmony_ci .opacity(this.isRenderSpecialImage()) // if the Image is rendered, it will call the function 248e41f4b71Sopenharmony_ci } 249e41f4b71Sopenharmony_ci} 250e41f4b71Sopenharmony_ci@Component 251e41f4b71Sopenharmony_cistruct CompA { 252e41f4b71Sopenharmony_ci @ObjectLink uiStyle: UIStyle 253e41f4b71Sopenharmony_ci // the following functions are used to show whether the component is called to be rendered 254e41f4b71Sopenharmony_ci private isRenderColumn() : number { 255e41f4b71Sopenharmony_ci console.log("Column is rendered"); 256e41f4b71Sopenharmony_ci return 1; 257e41f4b71Sopenharmony_ci } 258e41f4b71Sopenharmony_ci private isRenderStack() : number { 259e41f4b71Sopenharmony_ci console.log("Stack is rendered"); 260e41f4b71Sopenharmony_ci return 1; 261e41f4b71Sopenharmony_ci } 262e41f4b71Sopenharmony_ci private isRenderImage() : number { 263e41f4b71Sopenharmony_ci console.log("Image is rendered"); 264e41f4b71Sopenharmony_ci return 1; 265e41f4b71Sopenharmony_ci } 266e41f4b71Sopenharmony_ci private isRenderText() : number { 267e41f4b71Sopenharmony_ci console.log("Text is rendered"); 268e41f4b71Sopenharmony_ci return 1; 269e41f4b71Sopenharmony_ci } 270e41f4b71Sopenharmony_ci build() { 271e41f4b71Sopenharmony_ci Column() { 272e41f4b71Sopenharmony_ci SpecialImage({ 273e41f4b71Sopenharmony_ci // in low version, Dev Eco may throw a warning 274e41f4b71Sopenharmony_ci // But you can still build and run the code 275e41f4b71Sopenharmony_ci uiStyle: this.uiStyle 276e41f4b71Sopenharmony_ci }) 277e41f4b71Sopenharmony_ci Stack() { 278e41f4b71Sopenharmony_ci Column() { 279e41f4b71Sopenharmony_ci Image($r('app.media.icon')) // Use app.media.app_icon since API version 12. 280e41f4b71Sopenharmony_ci .opacity(this.uiStyle.alpha) 281e41f4b71Sopenharmony_ci .scale({ 282e41f4b71Sopenharmony_ci x: this.uiStyle.scaleX, 283e41f4b71Sopenharmony_ci y: this.uiStyle.scaleY 284e41f4b71Sopenharmony_ci }) 285e41f4b71Sopenharmony_ci .padding(this.isRenderImage()) 286e41f4b71Sopenharmony_ci .width(300) 287e41f4b71Sopenharmony_ci .height(300) 288e41f4b71Sopenharmony_ci } 289e41f4b71Sopenharmony_ci .width('100%') 290e41f4b71Sopenharmony_ci .position({ y: -80 }) 291e41f4b71Sopenharmony_ci Stack() { 292e41f4b71Sopenharmony_ci Text("Hello World") 293e41f4b71Sopenharmony_ci .fontColor("#182431") 294e41f4b71Sopenharmony_ci .fontWeight(FontWeight.Medium) 295e41f4b71Sopenharmony_ci .fontSize(this.uiStyle.fontSize) 296e41f4b71Sopenharmony_ci .opacity(this.isRenderText()) 297e41f4b71Sopenharmony_ci .margin({ top: 12 }) 298e41f4b71Sopenharmony_ci } 299e41f4b71Sopenharmony_ci .opacity(this.isRenderStack()) 300e41f4b71Sopenharmony_ci .position({ 301e41f4b71Sopenharmony_ci x: this.uiStyle.posX, 302e41f4b71Sopenharmony_ci y: this.uiStyle.posY 303e41f4b71Sopenharmony_ci }) 304e41f4b71Sopenharmony_ci .width('100%') 305e41f4b71Sopenharmony_ci .height('100%') 306e41f4b71Sopenharmony_ci } 307e41f4b71Sopenharmony_ci .margin({ top: 50 }) 308e41f4b71Sopenharmony_ci .borderRadius(this.uiStyle.borderRadius) 309e41f4b71Sopenharmony_ci .opacity(this.isRenderStack()) 310e41f4b71Sopenharmony_ci .backgroundColor("#FFFFFF") 311e41f4b71Sopenharmony_ci .width(this.uiStyle.width) 312e41f4b71Sopenharmony_ci .height(this.uiStyle.height) 313e41f4b71Sopenharmony_ci .translate({ 314e41f4b71Sopenharmony_ci x: this.uiStyle.translateX, 315e41f4b71Sopenharmony_ci y: this.uiStyle.translateY 316e41f4b71Sopenharmony_ci }) 317e41f4b71Sopenharmony_ci Column() { 318e41f4b71Sopenharmony_ci Button("Move") 319e41f4b71Sopenharmony_ci .width(312) 320e41f4b71Sopenharmony_ci .fontSize(20) 321e41f4b71Sopenharmony_ci .backgroundColor("#FF007DFF") 322e41f4b71Sopenharmony_ci .margin({ bottom: 10 }) 323e41f4b71Sopenharmony_ci .onClick(() => { 324e41f4b71Sopenharmony_ci animateTo({ 325e41f4b71Sopenharmony_ci duration: 500 326e41f4b71Sopenharmony_ci },() => { 327e41f4b71Sopenharmony_ci this.uiStyle.translateY = (this.uiStyle.translateY + 180) % 250; 328e41f4b71Sopenharmony_ci }) 329e41f4b71Sopenharmony_ci }) 330e41f4b71Sopenharmony_ci Button("Scale") 331e41f4b71Sopenharmony_ci .borderRadius(20) 332e41f4b71Sopenharmony_ci .backgroundColor("#FF007DFF") 333e41f4b71Sopenharmony_ci .fontSize(20) 334e41f4b71Sopenharmony_ci .width(312) 335e41f4b71Sopenharmony_ci .onClick(() => { 336e41f4b71Sopenharmony_ci this.uiStyle.scaleX = (this.uiStyle.scaleX + 0.6) % 0.8; 337e41f4b71Sopenharmony_ci }) 338e41f4b71Sopenharmony_ci } 339e41f4b71Sopenharmony_ci .position({ 340e41f4b71Sopenharmony_ci y:666 341e41f4b71Sopenharmony_ci }) 342e41f4b71Sopenharmony_ci .height('100%') 343e41f4b71Sopenharmony_ci .width('100%') 344e41f4b71Sopenharmony_ci 345e41f4b71Sopenharmony_ci } 346e41f4b71Sopenharmony_ci .opacity(this.isRenderColumn()) 347e41f4b71Sopenharmony_ci .width('100%') 348e41f4b71Sopenharmony_ci .height('100%') 349e41f4b71Sopenharmony_ci 350e41f4b71Sopenharmony_ci } 351e41f4b71Sopenharmony_ci} 352e41f4b71Sopenharmony_ci@Entry 353e41f4b71Sopenharmony_ci@Component 354e41f4b71Sopenharmony_cistruct Page { 355e41f4b71Sopenharmony_ci @State uiStyle: UIStyle = new UIStyle(); 356e41f4b71Sopenharmony_ci build() { 357e41f4b71Sopenharmony_ci Stack() { 358e41f4b71Sopenharmony_ci CompA({ 359e41f4b71Sopenharmony_ci // in low version, Dev Eco may throw a warning 360e41f4b71Sopenharmony_ci // But you can still build and run the code 361e41f4b71Sopenharmony_ci uiStyle: this.uiStyle 362e41f4b71Sopenharmony_ci }) 363e41f4b71Sopenharmony_ci } 364e41f4b71Sopenharmony_ci .backgroundColor("#F1F3F5") 365e41f4b71Sopenharmony_ci } 366e41f4b71Sopenharmony_ci} 367e41f4b71Sopenharmony_ci``` 368e41f4b71Sopenharmony_ci 369e41f4b71Sopenharmony_ciBelow you can see how the preceding code snippet works. 370e41f4b71Sopenharmony_ci 371e41f4b71Sopenharmony_ci 372e41f4b71Sopenharmony_ci 373e41f4b71Sopenharmony_ciClick the **Move** button before optimization. The duration for updating dirty nodes is as follows. 374e41f4b71Sopenharmony_ci 375e41f4b71Sopenharmony_ci 376e41f4b71Sopenharmony_ci 377e41f4b71Sopenharmony_ciIn the above example, **uiStyle** defines multiple properties, which are each associated with multiple components. When some of these properties are changed at the click of a button, all the components associated with **uiStyle** are re-rendered, even though they do not need to (because the properties of these components are not changed). The re-renders of these components can be observed through a series of defined **isRender** functions. When **Move** is clicked to perform the translation animation, the value of **translateY** changes multiple times. As a result, redundant re-renders occur at each frame, which greatly worsen the application performance. 378e41f4b71Sopenharmony_ci 379e41f4b71Sopenharmony_ciSuch redundant re-renders result from an update mechanism of the state management: If multiple properties of a class are bound to different components through an object of the class, then, if any of the properties is changed, the component associated with the property is re-rendered, together with components associated with the other properties, even though the other properties do not change. 380e41f4b71Sopenharmony_ci 381e41f4b71Sopenharmony_ciNaturally, this update mechanism brings down the re-rendering performance, especially in the case of a large, complex object associated with a considerable number of components. To fix this issue, split a large, complex object into a set of multiple small objects. In this way, redundant re-renders are reduced and the render scope precisely controlled, while the original code structure is retained. 382e41f4b71Sopenharmony_ci 383e41f4b71Sopenharmony_ci```typescript 384e41f4b71Sopenharmony_ci@Observed 385e41f4b71Sopenharmony_ciclass NeedRenderImage { // properties only used in the same component can be divided into the same new divided class 386e41f4b71Sopenharmony_ci public translateImageX: number = 0; 387e41f4b71Sopenharmony_ci public translateImageY: number = 0; 388e41f4b71Sopenharmony_ci public imageWidth:number = 78; 389e41f4b71Sopenharmony_ci public imageHeight:number = 78; 390e41f4b71Sopenharmony_ci} 391e41f4b71Sopenharmony_ci@Observed 392e41f4b71Sopenharmony_ciclass NeedRenderScale { // properties usually used together can be divided into the same new divided class 393e41f4b71Sopenharmony_ci public scaleX: number = 0.3; 394e41f4b71Sopenharmony_ci public scaleY: number = 0.3; 395e41f4b71Sopenharmony_ci} 396e41f4b71Sopenharmony_ci@Observed 397e41f4b71Sopenharmony_ciclass NeedRenderAlpha { // properties that may be used in different places can be divided into the same new divided class 398e41f4b71Sopenharmony_ci public alpha: number = 0.5; 399e41f4b71Sopenharmony_ci} 400e41f4b71Sopenharmony_ci@Observed 401e41f4b71Sopenharmony_ciclass NeedRenderSize { // properties usually used together can be divided into the same new divided class 402e41f4b71Sopenharmony_ci public width: number = 336; 403e41f4b71Sopenharmony_ci public height: number = 178; 404e41f4b71Sopenharmony_ci} 405e41f4b71Sopenharmony_ci@Observed 406e41f4b71Sopenharmony_ciclass NeedRenderPos { // properties usually used together can be divided into the same new divided class 407e41f4b71Sopenharmony_ci public posX: number = 10; 408e41f4b71Sopenharmony_ci public posY: number = 50; 409e41f4b71Sopenharmony_ci} 410e41f4b71Sopenharmony_ci@Observed 411e41f4b71Sopenharmony_ciclass NeedRenderBorderRadius { // properties that may be used in different places can be divided into the same new divided class 412e41f4b71Sopenharmony_ci public borderRadius: number = 24; 413e41f4b71Sopenharmony_ci} 414e41f4b71Sopenharmony_ci@Observed 415e41f4b71Sopenharmony_ciclass NeedRenderFontSize { // properties that may be used in different places can be divided into the same new divided class 416e41f4b71Sopenharmony_ci public fontSize: number = 20; 417e41f4b71Sopenharmony_ci} 418e41f4b71Sopenharmony_ci@Observed 419e41f4b71Sopenharmony_ciclass NeedRenderTranslate { // properties usually used together can be divided into the same new divided class 420e41f4b71Sopenharmony_ci public translateX: number = 0; 421e41f4b71Sopenharmony_ci public translateY: number = 0; 422e41f4b71Sopenharmony_ci} 423e41f4b71Sopenharmony_ci@Observed 424e41f4b71Sopenharmony_ciclass UIStyle { 425e41f4b71Sopenharmony_ci // define new variable instead of using old one 426e41f4b71Sopenharmony_ci needRenderTranslate: NeedRenderTranslate = new NeedRenderTranslate(); 427e41f4b71Sopenharmony_ci needRenderFontSize: NeedRenderFontSize = new NeedRenderFontSize(); 428e41f4b71Sopenharmony_ci needRenderBorderRadius: NeedRenderBorderRadius = new NeedRenderBorderRadius(); 429e41f4b71Sopenharmony_ci needRenderPos: NeedRenderPos = new NeedRenderPos(); 430e41f4b71Sopenharmony_ci needRenderSize: NeedRenderSize = new NeedRenderSize(); 431e41f4b71Sopenharmony_ci needRenderAlpha: NeedRenderAlpha = new NeedRenderAlpha(); 432e41f4b71Sopenharmony_ci needRenderScale: NeedRenderScale = new NeedRenderScale(); 433e41f4b71Sopenharmony_ci needRenderImage: NeedRenderImage = new NeedRenderImage(); 434e41f4b71Sopenharmony_ci} 435e41f4b71Sopenharmony_ci@Component 436e41f4b71Sopenharmony_cistruct SpecialImage { 437e41f4b71Sopenharmony_ci @ObjectLink uiStyle : UIStyle; 438e41f4b71Sopenharmony_ci @ObjectLink needRenderImage: NeedRenderImage // receive the new class from its parent component 439e41f4b71Sopenharmony_ci private isRenderSpecialImage() : number { // function to show whether the component is rendered 440e41f4b71Sopenharmony_ci console.log("SpecialImage is rendered"); 441e41f4b71Sopenharmony_ci return 1; 442e41f4b71Sopenharmony_ci } 443e41f4b71Sopenharmony_ci build() { 444e41f4b71Sopenharmony_ci Image($r('app.media.icon')) // Use app.media.app_icon since API version 12. 445e41f4b71Sopenharmony_ci .width(this.needRenderImage.imageWidth) // !! use this.needRenderImage.xxx rather than this.uiStyle.needRenderImage.xxx !! 446e41f4b71Sopenharmony_ci .height(this.needRenderImage.imageHeight) 447e41f4b71Sopenharmony_ci .margin({top:20}) 448e41f4b71Sopenharmony_ci .translate({ 449e41f4b71Sopenharmony_ci x: this.needRenderImage.translateImageX, 450e41f4b71Sopenharmony_ci y: this.needRenderImage.translateImageY 451e41f4b71Sopenharmony_ci }) 452e41f4b71Sopenharmony_ci .opacity(this.isRenderSpecialImage()) // if the Image is rendered, it will call the function 453e41f4b71Sopenharmony_ci } 454e41f4b71Sopenharmony_ci} 455e41f4b71Sopenharmony_ci@Component 456e41f4b71Sopenharmony_cistruct CompA { 457e41f4b71Sopenharmony_ci @ObjectLink uiStyle: UIStyle; 458e41f4b71Sopenharmony_ci @ObjectLink needRenderTranslate: NeedRenderTranslate; // receive the new class from its parent component 459e41f4b71Sopenharmony_ci @ObjectLink needRenderFontSize: NeedRenderFontSize; 460e41f4b71Sopenharmony_ci @ObjectLink needRenderBorderRadius: NeedRenderBorderRadius; 461e41f4b71Sopenharmony_ci @ObjectLink needRenderPos: NeedRenderPos; 462e41f4b71Sopenharmony_ci @ObjectLink needRenderSize: NeedRenderSize; 463e41f4b71Sopenharmony_ci @ObjectLink needRenderAlpha: NeedRenderAlpha; 464e41f4b71Sopenharmony_ci @ObjectLink needRenderScale: NeedRenderScale; 465e41f4b71Sopenharmony_ci // the following functions are used to show whether the component is called to be rendered 466e41f4b71Sopenharmony_ci private isRenderColumn() : number { 467e41f4b71Sopenharmony_ci console.log("Column is rendered"); 468e41f4b71Sopenharmony_ci return 1; 469e41f4b71Sopenharmony_ci } 470e41f4b71Sopenharmony_ci private isRenderStack() : number { 471e41f4b71Sopenharmony_ci console.log("Stack is rendered"); 472e41f4b71Sopenharmony_ci return 1; 473e41f4b71Sopenharmony_ci } 474e41f4b71Sopenharmony_ci private isRenderImage() : number { 475e41f4b71Sopenharmony_ci console.log("Image is rendered"); 476e41f4b71Sopenharmony_ci return 1; 477e41f4b71Sopenharmony_ci } 478e41f4b71Sopenharmony_ci private isRenderText() : number { 479e41f4b71Sopenharmony_ci console.log("Text is rendered"); 480e41f4b71Sopenharmony_ci return 1; 481e41f4b71Sopenharmony_ci } 482e41f4b71Sopenharmony_ci build() { 483e41f4b71Sopenharmony_ci Column() { 484e41f4b71Sopenharmony_ci SpecialImage({ 485e41f4b71Sopenharmony_ci // in low version, Dev Eco may throw a warning 486e41f4b71Sopenharmony_ci // But you can still build and run the code 487e41f4b71Sopenharmony_ci uiStyle: this.uiStyle, 488e41f4b71Sopenharmony_ci needRenderImage: this.uiStyle.needRenderImage //send it to its child 489e41f4b71Sopenharmony_ci }) 490e41f4b71Sopenharmony_ci Stack() { 491e41f4b71Sopenharmony_ci Column() { 492e41f4b71Sopenharmony_ci Image($r('app.media.icon')) // Use app.media.app_icon since API version 12. 493e41f4b71Sopenharmony_ci .opacity(this.needRenderAlpha.alpha) 494e41f4b71Sopenharmony_ci .scale({ 495e41f4b71Sopenharmony_ci x: this.needRenderScale.scaleX, // use this.needRenderXxx.xxx rather than this.uiStyle.needRenderXxx.xxx 496e41f4b71Sopenharmony_ci y: this.needRenderScale.scaleY 497e41f4b71Sopenharmony_ci }) 498e41f4b71Sopenharmony_ci .padding(this.isRenderImage()) 499e41f4b71Sopenharmony_ci .width(300) 500e41f4b71Sopenharmony_ci .height(300) 501e41f4b71Sopenharmony_ci } 502e41f4b71Sopenharmony_ci .width('100%') 503e41f4b71Sopenharmony_ci .position({ y: -80 }) 504e41f4b71Sopenharmony_ci 505e41f4b71Sopenharmony_ci Stack() { 506e41f4b71Sopenharmony_ci Text("Hello World") 507e41f4b71Sopenharmony_ci .fontColor("#182431") 508e41f4b71Sopenharmony_ci .fontWeight(FontWeight.Medium) 509e41f4b71Sopenharmony_ci .fontSize(this.needRenderFontSize.fontSize) 510e41f4b71Sopenharmony_ci .opacity(this.isRenderText()) 511e41f4b71Sopenharmony_ci .margin({ top: 12 }) 512e41f4b71Sopenharmony_ci } 513e41f4b71Sopenharmony_ci .opacity(this.isRenderStack()) 514e41f4b71Sopenharmony_ci .position({ 515e41f4b71Sopenharmony_ci x: this.needRenderPos.posX, 516e41f4b71Sopenharmony_ci y: this.needRenderPos.posY 517e41f4b71Sopenharmony_ci }) 518e41f4b71Sopenharmony_ci .width('100%') 519e41f4b71Sopenharmony_ci .height('100%') 520e41f4b71Sopenharmony_ci } 521e41f4b71Sopenharmony_ci .margin({ top: 50 }) 522e41f4b71Sopenharmony_ci .borderRadius(this.needRenderBorderRadius.borderRadius) 523e41f4b71Sopenharmony_ci .opacity(this.isRenderStack()) 524e41f4b71Sopenharmony_ci .backgroundColor("#FFFFFF") 525e41f4b71Sopenharmony_ci .width(this.needRenderSize.width) 526e41f4b71Sopenharmony_ci .height(this.needRenderSize.height) 527e41f4b71Sopenharmony_ci .translate({ 528e41f4b71Sopenharmony_ci x: this.needRenderTranslate.translateX, 529e41f4b71Sopenharmony_ci y: this.needRenderTranslate.translateY 530e41f4b71Sopenharmony_ci }) 531e41f4b71Sopenharmony_ci 532e41f4b71Sopenharmony_ci Column() { 533e41f4b71Sopenharmony_ci Button("Move") 534e41f4b71Sopenharmony_ci .width(312) 535e41f4b71Sopenharmony_ci .fontSize(20) 536e41f4b71Sopenharmony_ci .backgroundColor("#FF007DFF") 537e41f4b71Sopenharmony_ci .margin({ bottom: 10 }) 538e41f4b71Sopenharmony_ci .onClick(() => { 539e41f4b71Sopenharmony_ci animateTo({ 540e41f4b71Sopenharmony_ci duration: 500 541e41f4b71Sopenharmony_ci }, () => { 542e41f4b71Sopenharmony_ci this.needRenderTranslate.translateY = (this.needRenderTranslate.translateY + 180) % 250; 543e41f4b71Sopenharmony_ci }) 544e41f4b71Sopenharmony_ci }) 545e41f4b71Sopenharmony_ci Button("Scale") 546e41f4b71Sopenharmony_ci .borderRadius(20) 547e41f4b71Sopenharmony_ci .backgroundColor("#FF007DFF") 548e41f4b71Sopenharmony_ci .fontSize(20) 549e41f4b71Sopenharmony_ci .width(312) 550e41f4b71Sopenharmony_ci .margin({ bottom: 10 }) 551e41f4b71Sopenharmony_ci .onClick(() => { 552e41f4b71Sopenharmony_ci this.needRenderScale.scaleX = (this.needRenderScale.scaleX + 0.6) % 0.8; 553e41f4b71Sopenharmony_ci }) 554e41f4b71Sopenharmony_ci Button("Change Image") 555e41f4b71Sopenharmony_ci .borderRadius(20) 556e41f4b71Sopenharmony_ci .backgroundColor("#FF007DFF") 557e41f4b71Sopenharmony_ci .fontSize(20) 558e41f4b71Sopenharmony_ci .width(312) 559e41f4b71Sopenharmony_ci .onClick(() => { // in the parent component, still use this.uiStyle.needRenderXxx.xxx to change the properties 560e41f4b71Sopenharmony_ci this.uiStyle.needRenderImage.imageWidth = (this.uiStyle.needRenderImage.imageWidth + 30) % 160; 561e41f4b71Sopenharmony_ci this.uiStyle.needRenderImage.imageHeight = (this.uiStyle.needRenderImage.imageHeight + 30) % 160; 562e41f4b71Sopenharmony_ci }) 563e41f4b71Sopenharmony_ci } 564e41f4b71Sopenharmony_ci .position({ 565e41f4b71Sopenharmony_ci y: 616 566e41f4b71Sopenharmony_ci }) 567e41f4b71Sopenharmony_ci .height('100%') 568e41f4b71Sopenharmony_ci .width('100%') 569e41f4b71Sopenharmony_ci } 570e41f4b71Sopenharmony_ci .opacity(this.isRenderColumn()) 571e41f4b71Sopenharmony_ci .width('100%') 572e41f4b71Sopenharmony_ci .height('100%') 573e41f4b71Sopenharmony_ci } 574e41f4b71Sopenharmony_ci} 575e41f4b71Sopenharmony_ci@Entry 576e41f4b71Sopenharmony_ci@Component 577e41f4b71Sopenharmony_cistruct Page { 578e41f4b71Sopenharmony_ci @State uiStyle: UIStyle = new UIStyle(); 579e41f4b71Sopenharmony_ci build() { 580e41f4b71Sopenharmony_ci Stack() { 581e41f4b71Sopenharmony_ci CompA({ 582e41f4b71Sopenharmony_ci // in low version, Dev Eco may throw a warning 583e41f4b71Sopenharmony_ci // But you can still build and run the code 584e41f4b71Sopenharmony_ci uiStyle: this.uiStyle, 585e41f4b71Sopenharmony_ci needRenderTranslate: this.uiStyle.needRenderTranslate, //send all the new class child need 586e41f4b71Sopenharmony_ci needRenderFontSize: this.uiStyle.needRenderFontSize, 587e41f4b71Sopenharmony_ci needRenderBorderRadius: this.uiStyle.needRenderBorderRadius, 588e41f4b71Sopenharmony_ci needRenderPos: this.uiStyle.needRenderPos, 589e41f4b71Sopenharmony_ci needRenderSize: this.uiStyle.needRenderSize, 590e41f4b71Sopenharmony_ci needRenderAlpha: this.uiStyle.needRenderAlpha, 591e41f4b71Sopenharmony_ci needRenderScale: this.uiStyle.needRenderScale 592e41f4b71Sopenharmony_ci }) 593e41f4b71Sopenharmony_ci } 594e41f4b71Sopenharmony_ci .backgroundColor("#F1F3F5") 595e41f4b71Sopenharmony_ci } 596e41f4b71Sopenharmony_ci} 597e41f4b71Sopenharmony_ci``` 598e41f4b71Sopenharmony_ci 599e41f4b71Sopenharmony_ciBelow you can see how the preceding code snippet works. 600e41f4b71Sopenharmony_ci 601e41f4b71Sopenharmony_ciClick the **Move** button after optimization. The duration for updating dirty nodes is as follows. 602e41f4b71Sopenharmony_ci 603e41f4b71Sopenharmony_ci 604e41f4b71Sopenharmony_ci 605e41f4b71Sopenharmony_ciAfter the optimization, the 15 attributes previously in one class are divided into eight classes, and the bound components are adapted accordingly. The division of properties complies with the following principles: 606e41f4b71Sopenharmony_ci 607e41f4b71Sopenharmony_ci- Properties that are only used in the same component can be divided into the same new child class, that is, **NeedRenderImage** in the example. This mode of division is applicable to the scenario where components are frequently re-rendered due to changes of unassociated properties. 608e41f4b71Sopenharmony_ci- Properties that are frequently used together can be divided into the same new child class, that is, **NeedRenderScale**, **NeedRenderTranslate**, **NeedRenderPos**, and **NeedRenderSize** in the example. This mode of division is applicable to the scenario where properties often appear in pairs or are applied to the same style, for example, **.translate**, **.position**, and **.scale** (which usually receive an object as a parameter). 609e41f4b71Sopenharmony_ci- Properties that may be used in different places should be divided into a new child class, that is, **NeedRenderAlpha**, **NeedRenderBorderRadius**, and **NeedRenderFontSize** in the example. This mode of division is applicable to the scenario where a property works on multiple components or works on their own, for example, **.opacity** and **.borderRadius** (which usually work on their own). 610e41f4b71Sopenharmony_ci 611e41f4b71Sopenharmony_ciAs in combination of properties, the principle behind division of properties is that changes to properties of objects nested more than two levels deep cannot be observed. Yet, you can use @Observed and @ObjectLink to transfer level-2 objects between parent and child nodes to observe property changes at level 2 and precisely control the render scope. <!--Del-->For details about the division of properties, see [Precisely Controlling Render Scope](https://gitee.com/openharmony/docs/blob/master/en/application-dev/performance/precisely-control-render-scope.md).<!--DelEnd--> 612e41f4b71Sopenharmony_ci 613e41f4b71Sopenharmony_ci@Track decorator can also precisely control the render scope, which does not involve division of properties. 614e41f4b71Sopenharmony_ci 615e41f4b71Sopenharmony_ci```ts 616e41f4b71Sopenharmony_ci@Observed 617e41f4b71Sopenharmony_ciclass UIStyle { 618e41f4b71Sopenharmony_ci @Track translateX: number = 0; 619e41f4b71Sopenharmony_ci @Track translateY: number = 0; 620e41f4b71Sopenharmony_ci @Track scaleX: number = 0.3; 621e41f4b71Sopenharmony_ci @Track scaleY: number = 0.3; 622e41f4b71Sopenharmony_ci @Track width: number = 336; 623e41f4b71Sopenharmony_ci @Track height: number = 178; 624e41f4b71Sopenharmony_ci @Track posX: number = 10; 625e41f4b71Sopenharmony_ci @Track posY: number = 50; 626e41f4b71Sopenharmony_ci @Track alpha: number = 0.5; 627e41f4b71Sopenharmony_ci @Track borderRadius: number = 24; 628e41f4b71Sopenharmony_ci @Track imageWidth: number = 78; 629e41f4b71Sopenharmony_ci @Track imageHeight: number = 78; 630e41f4b71Sopenharmony_ci @Track translateImageX: number = 0; 631e41f4b71Sopenharmony_ci @Track translateImageY: number = 0; 632e41f4b71Sopenharmony_ci @Track fontSize: number = 20; 633e41f4b71Sopenharmony_ci} 634e41f4b71Sopenharmony_ci@Component 635e41f4b71Sopenharmony_cistruct SpecialImage { 636e41f4b71Sopenharmony_ci @ObjectLink uiStyle: UIStyle; 637e41f4b71Sopenharmony_ci private isRenderSpecialImage() : number { // function to show whether the component is rendered 638e41f4b71Sopenharmony_ci console.log("SpecialImage is rendered"); 639e41f4b71Sopenharmony_ci return 1; 640e41f4b71Sopenharmony_ci } 641e41f4b71Sopenharmony_ci build() { 642e41f4b71Sopenharmony_ci Image($r('app.media.icon')) // Use app.media.app_icon since API version 12. 643e41f4b71Sopenharmony_ci .width(this.uiStyle.imageWidth) 644e41f4b71Sopenharmony_ci .height(this.uiStyle.imageHeight) 645e41f4b71Sopenharmony_ci .margin({ top: 20 }) 646e41f4b71Sopenharmony_ci .translate({ 647e41f4b71Sopenharmony_ci x: this.uiStyle.translateImageX, 648e41f4b71Sopenharmony_ci y: this.uiStyle.translateImageY 649e41f4b71Sopenharmony_ci }) 650e41f4b71Sopenharmony_ci .opacity(this.isRenderSpecialImage()) // if the Image is rendered, it will call the function 651e41f4b71Sopenharmony_ci } 652e41f4b71Sopenharmony_ci} 653e41f4b71Sopenharmony_ci@Component 654e41f4b71Sopenharmony_cistruct CompA { 655e41f4b71Sopenharmony_ci @ObjectLink uiStyle: UIStyle 656e41f4b71Sopenharmony_ci // the following functions are used to show whether the component is called to be rendered 657e41f4b71Sopenharmony_ci private isRenderColumn() : number { 658e41f4b71Sopenharmony_ci console.log("Column is rendered"); 659e41f4b71Sopenharmony_ci return 1; 660e41f4b71Sopenharmony_ci } 661e41f4b71Sopenharmony_ci private isRenderStack() : number { 662e41f4b71Sopenharmony_ci console.log("Stack is rendered"); 663e41f4b71Sopenharmony_ci return 1; 664e41f4b71Sopenharmony_ci } 665e41f4b71Sopenharmony_ci private isRenderImage() : number { 666e41f4b71Sopenharmony_ci console.log("Image is rendered"); 667e41f4b71Sopenharmony_ci return 1; 668e41f4b71Sopenharmony_ci } 669e41f4b71Sopenharmony_ci private isRenderText() : number { 670e41f4b71Sopenharmony_ci console.log("Text is rendered"); 671e41f4b71Sopenharmony_ci return 1; 672e41f4b71Sopenharmony_ci } 673e41f4b71Sopenharmony_ci build() { 674e41f4b71Sopenharmony_ci Column() { 675e41f4b71Sopenharmony_ci SpecialImage({ 676e41f4b71Sopenharmony_ci // in low version, Dev Eco may throw a warning 677e41f4b71Sopenharmony_ci // But you can still build and run the code 678e41f4b71Sopenharmony_ci uiStyle: this.uiStyle 679e41f4b71Sopenharmony_ci }) 680e41f4b71Sopenharmony_ci Stack() { 681e41f4b71Sopenharmony_ci Column() { 682e41f4b71Sopenharmony_ci Image($r('app.media.icon')) // Use app.media.app_icon since API version 12. 683e41f4b71Sopenharmony_ci .opacity(this.uiStyle.alpha) 684e41f4b71Sopenharmony_ci .scale({ 685e41f4b71Sopenharmony_ci x: this.uiStyle.scaleX, 686e41f4b71Sopenharmony_ci y: this.uiStyle.scaleY 687e41f4b71Sopenharmony_ci }) 688e41f4b71Sopenharmony_ci .padding(this.isRenderImage()) 689e41f4b71Sopenharmony_ci .width(300) 690e41f4b71Sopenharmony_ci .height(300) 691e41f4b71Sopenharmony_ci } 692e41f4b71Sopenharmony_ci .width('100%') 693e41f4b71Sopenharmony_ci .position({ y: -80 }) 694e41f4b71Sopenharmony_ci Stack() { 695e41f4b71Sopenharmony_ci Text("Hello World") 696e41f4b71Sopenharmony_ci .fontColor("#182431") 697e41f4b71Sopenharmony_ci .fontWeight(FontWeight.Medium) 698e41f4b71Sopenharmony_ci .fontSize(this.uiStyle.fontSize) 699e41f4b71Sopenharmony_ci .opacity(this.isRenderText()) 700e41f4b71Sopenharmony_ci .margin({ top: 12 }) 701e41f4b71Sopenharmony_ci } 702e41f4b71Sopenharmony_ci .opacity(this.isRenderStack()) 703e41f4b71Sopenharmony_ci .position({ 704e41f4b71Sopenharmony_ci x: this.uiStyle.posX, 705e41f4b71Sopenharmony_ci y: this.uiStyle.posY 706e41f4b71Sopenharmony_ci }) 707e41f4b71Sopenharmony_ci .width('100%') 708e41f4b71Sopenharmony_ci .height('100%') 709e41f4b71Sopenharmony_ci } 710e41f4b71Sopenharmony_ci .margin({ top: 50 }) 711e41f4b71Sopenharmony_ci .borderRadius(this.uiStyle.borderRadius) 712e41f4b71Sopenharmony_ci .opacity(this.isRenderStack()) 713e41f4b71Sopenharmony_ci .backgroundColor("#FFFFFF") 714e41f4b71Sopenharmony_ci .width(this.uiStyle.width) 715e41f4b71Sopenharmony_ci .height(this.uiStyle.height) 716e41f4b71Sopenharmony_ci .translate({ 717e41f4b71Sopenharmony_ci x: this.uiStyle.translateX, 718e41f4b71Sopenharmony_ci y: this.uiStyle.translateY 719e41f4b71Sopenharmony_ci }) 720e41f4b71Sopenharmony_ci Column() { 721e41f4b71Sopenharmony_ci Button("Move") 722e41f4b71Sopenharmony_ci .width(312) 723e41f4b71Sopenharmony_ci .fontSize(20) 724e41f4b71Sopenharmony_ci .backgroundColor("#FF007DFF") 725e41f4b71Sopenharmony_ci .margin({ bottom: 10 }) 726e41f4b71Sopenharmony_ci .onClick(() => { 727e41f4b71Sopenharmony_ci animateTo({ 728e41f4b71Sopenharmony_ci duration: 500 729e41f4b71Sopenharmony_ci },() => { 730e41f4b71Sopenharmony_ci this.uiStyle.translateY = (this.uiStyle.translateY + 180) % 250; 731e41f4b71Sopenharmony_ci }) 732e41f4b71Sopenharmony_ci }) 733e41f4b71Sopenharmony_ci Button("Scale") 734e41f4b71Sopenharmony_ci .borderRadius(20) 735e41f4b71Sopenharmony_ci .backgroundColor("#FF007DFF") 736e41f4b71Sopenharmony_ci .fontSize(20) 737e41f4b71Sopenharmony_ci .width(312) 738e41f4b71Sopenharmony_ci .onClick(() => { 739e41f4b71Sopenharmony_ci this.uiStyle.scaleX = (this.uiStyle.scaleX + 0.6) % 0.8; 740e41f4b71Sopenharmony_ci }) 741e41f4b71Sopenharmony_ci } 742e41f4b71Sopenharmony_ci .position({ 743e41f4b71Sopenharmony_ci y:666 744e41f4b71Sopenharmony_ci }) 745e41f4b71Sopenharmony_ci .height('100%') 746e41f4b71Sopenharmony_ci .width('100%') 747e41f4b71Sopenharmony_ci 748e41f4b71Sopenharmony_ci } 749e41f4b71Sopenharmony_ci .opacity(this.isRenderColumn()) 750e41f4b71Sopenharmony_ci .width('100%') 751e41f4b71Sopenharmony_ci .height('100%') 752e41f4b71Sopenharmony_ci 753e41f4b71Sopenharmony_ci } 754e41f4b71Sopenharmony_ci} 755e41f4b71Sopenharmony_ci@Entry 756e41f4b71Sopenharmony_ci@Component 757e41f4b71Sopenharmony_cistruct Page { 758e41f4b71Sopenharmony_ci @State uiStyle: UIStyle = new UIStyle(); 759e41f4b71Sopenharmony_ci build() { 760e41f4b71Sopenharmony_ci Stack() { 761e41f4b71Sopenharmony_ci CompA({ 762e41f4b71Sopenharmony_ci // in low version, Dev Eco may throw a warning 763e41f4b71Sopenharmony_ci // But you can still build and run the code 764e41f4b71Sopenharmony_ci uiStyle: this.uiStyle 765e41f4b71Sopenharmony_ci }) 766e41f4b71Sopenharmony_ci } 767e41f4b71Sopenharmony_ci .backgroundColor("#F1F3F5") 768e41f4b71Sopenharmony_ci } 769e41f4b71Sopenharmony_ci} 770e41f4b71Sopenharmony_ci``` 771e41f4b71Sopenharmony_ci 772e41f4b71Sopenharmony_ci### Binding Components to Class Objects Decorated with @Observed or Declared as State Variables 773e41f4b71Sopenharmony_ci 774e41f4b71Sopenharmony_ciYour application may sometimes allow users to reset data - by assigning a new object to the target state variable. The type of the new object is the trick here: If not handled carefully, it may result in the UI not being re-rendered as expected. 775e41f4b71Sopenharmony_ci 776e41f4b71Sopenharmony_ci```typescript 777e41f4b71Sopenharmony_ci@Observed 778e41f4b71Sopenharmony_ciclass Child { 779e41f4b71Sopenharmony_ci count: number; 780e41f4b71Sopenharmony_ci constructor(count: number) { 781e41f4b71Sopenharmony_ci this.count = count 782e41f4b71Sopenharmony_ci } 783e41f4b71Sopenharmony_ci} 784e41f4b71Sopenharmony_ci@Observed 785e41f4b71Sopenharmony_ciclass ChildList extends Array<Child> { 786e41f4b71Sopenharmony_ci}; 787e41f4b71Sopenharmony_ci@Observed 788e41f4b71Sopenharmony_ciclass Ancestor { 789e41f4b71Sopenharmony_ci childList: ChildList; 790e41f4b71Sopenharmony_ci constructor(childList: ChildList) { 791e41f4b71Sopenharmony_ci this.childList = childList; 792e41f4b71Sopenharmony_ci } 793e41f4b71Sopenharmony_ci public loadData() { 794e41f4b71Sopenharmony_ci let tempList = [new Child(1), new Child(2), new Child(3), new Child(4), new Child(5)]; 795e41f4b71Sopenharmony_ci this.childList = tempList; 796e41f4b71Sopenharmony_ci } 797e41f4b71Sopenharmony_ci 798e41f4b71Sopenharmony_ci public clearData() { 799e41f4b71Sopenharmony_ci this.childList = [] 800e41f4b71Sopenharmony_ci } 801e41f4b71Sopenharmony_ci} 802e41f4b71Sopenharmony_ci@Component 803e41f4b71Sopenharmony_cistruct CompChild { 804e41f4b71Sopenharmony_ci @Link childList: ChildList; 805e41f4b71Sopenharmony_ci @ObjectLink child: Child; 806e41f4b71Sopenharmony_ci 807e41f4b71Sopenharmony_ci build() { 808e41f4b71Sopenharmony_ci Row() { 809e41f4b71Sopenharmony_ci Text(this.child.count+'') 810e41f4b71Sopenharmony_ci .height(70) 811e41f4b71Sopenharmony_ci .fontSize(20) 812e41f4b71Sopenharmony_ci .borderRadius({ 813e41f4b71Sopenharmony_ci topLeft: 6, 814e41f4b71Sopenharmony_ci topRight: 6 815e41f4b71Sopenharmony_ci }) 816e41f4b71Sopenharmony_ci .margin({left: 50}) 817e41f4b71Sopenharmony_ci Button('X') 818e41f4b71Sopenharmony_ci .backgroundColor(Color.Red) 819e41f4b71Sopenharmony_ci .onClick(()=>{ 820e41f4b71Sopenharmony_ci let index = this.childList.findIndex((item) => { 821e41f4b71Sopenharmony_ci return item.count === this.child.count 822e41f4b71Sopenharmony_ci }) 823e41f4b71Sopenharmony_ci if (index !== -1) { 824e41f4b71Sopenharmony_ci this.childList.splice(index, 1); 825e41f4b71Sopenharmony_ci } 826e41f4b71Sopenharmony_ci }) 827e41f4b71Sopenharmony_ci .margin({ 828e41f4b71Sopenharmony_ci left: 200, 829e41f4b71Sopenharmony_ci right:30 830e41f4b71Sopenharmony_ci }) 831e41f4b71Sopenharmony_ci } 832e41f4b71Sopenharmony_ci .margin({ 833e41f4b71Sopenharmony_ci top:15, 834e41f4b71Sopenharmony_ci left: 15, 835e41f4b71Sopenharmony_ci right:10, 836e41f4b71Sopenharmony_ci bottom:15 837e41f4b71Sopenharmony_ci }) 838e41f4b71Sopenharmony_ci .borderRadius(6) 839e41f4b71Sopenharmony_ci .backgroundColor(Color.Grey) 840e41f4b71Sopenharmony_ci } 841e41f4b71Sopenharmony_ci} 842e41f4b71Sopenharmony_ci@Component 843e41f4b71Sopenharmony_cistruct CompList { 844e41f4b71Sopenharmony_ci @ObjectLink@Watch('changeChildList') childList: ChildList; 845e41f4b71Sopenharmony_ci 846e41f4b71Sopenharmony_ci changeChildList() { 847e41f4b71Sopenharmony_ci console.log('CompList ChildList change'); 848e41f4b71Sopenharmony_ci } 849e41f4b71Sopenharmony_ci 850e41f4b71Sopenharmony_ci isRenderCompChild(index: number) : number { 851e41f4b71Sopenharmony_ci console.log("Comp Child is render" + index); 852e41f4b71Sopenharmony_ci return 1; 853e41f4b71Sopenharmony_ci } 854e41f4b71Sopenharmony_ci 855e41f4b71Sopenharmony_ci build() { 856e41f4b71Sopenharmony_ci Column() { 857e41f4b71Sopenharmony_ci List() { 858e41f4b71Sopenharmony_ci ForEach(this.childList, (item: Child, index) => { 859e41f4b71Sopenharmony_ci ListItem() { 860e41f4b71Sopenharmony_ci // in low version, Dev Eco may throw a warning 861e41f4b71Sopenharmony_ci // But you can still build and run the code 862e41f4b71Sopenharmony_ci CompChild({ 863e41f4b71Sopenharmony_ci childList: this.childList, 864e41f4b71Sopenharmony_ci child: item 865e41f4b71Sopenharmony_ci }) 866e41f4b71Sopenharmony_ci .opacity(this.isRenderCompChild(index)) 867e41f4b71Sopenharmony_ci } 868e41f4b71Sopenharmony_ci 869e41f4b71Sopenharmony_ci }) 870e41f4b71Sopenharmony_ci } 871e41f4b71Sopenharmony_ci .height('70%') 872e41f4b71Sopenharmony_ci } 873e41f4b71Sopenharmony_ci } 874e41f4b71Sopenharmony_ci} 875e41f4b71Sopenharmony_ci@Component 876e41f4b71Sopenharmony_cistruct CompAncestor { 877e41f4b71Sopenharmony_ci @ObjectLink ancestor: Ancestor; 878e41f4b71Sopenharmony_ci 879e41f4b71Sopenharmony_ci build() { 880e41f4b71Sopenharmony_ci Column() { 881e41f4b71Sopenharmony_ci // in low version, Dev Eco may throw a warning 882e41f4b71Sopenharmony_ci // But you can still build and run the code 883e41f4b71Sopenharmony_ci CompList({ childList: this.ancestor.childList }) 884e41f4b71Sopenharmony_ci Row() { 885e41f4b71Sopenharmony_ci Button("Clear") 886e41f4b71Sopenharmony_ci .onClick(() => { 887e41f4b71Sopenharmony_ci this.ancestor.clearData() 888e41f4b71Sopenharmony_ci }) 889e41f4b71Sopenharmony_ci .width(100) 890e41f4b71Sopenharmony_ci .margin({right: 50}) 891e41f4b71Sopenharmony_ci Button("Recover") 892e41f4b71Sopenharmony_ci .onClick(() => { 893e41f4b71Sopenharmony_ci this.ancestor.loadData() 894e41f4b71Sopenharmony_ci }) 895e41f4b71Sopenharmony_ci .width(100) 896e41f4b71Sopenharmony_ci } 897e41f4b71Sopenharmony_ci } 898e41f4b71Sopenharmony_ci } 899e41f4b71Sopenharmony_ci} 900e41f4b71Sopenharmony_ci@Entry 901e41f4b71Sopenharmony_ci@Component 902e41f4b71Sopenharmony_cistruct Page { 903e41f4b71Sopenharmony_ci @State childList: ChildList = [new Child(1), new Child(2), new Child(3), new Child(4),new Child(5)]; 904e41f4b71Sopenharmony_ci @State ancestor: Ancestor = new Ancestor(this.childList) 905e41f4b71Sopenharmony_ci 906e41f4b71Sopenharmony_ci build() { 907e41f4b71Sopenharmony_ci Column() { 908e41f4b71Sopenharmony_ci // in low version, Dev Eco may throw a warning 909e41f4b71Sopenharmony_ci // But you can still build and run the code 910e41f4b71Sopenharmony_ci CompAncestor({ ancestor: this.ancestor}) 911e41f4b71Sopenharmony_ci } 912e41f4b71Sopenharmony_ci } 913e41f4b71Sopenharmony_ci} 914e41f4b71Sopenharmony_ci``` 915e41f4b71Sopenharmony_ci 916e41f4b71Sopenharmony_ciBelow you can see how the preceding code snippet works. 917e41f4b71Sopenharmony_ci 918e41f4b71Sopenharmony_ci 919e41f4b71Sopenharmony_ci 920e41f4b71Sopenharmony_ciIn the code there is a data source of the ChildList type. If you click **X** to delete some data and then click **Recover** to restore **ChildList**, the UI is not re-rendered after you click **X** again, and no "CompList ChildList change" log is printed. 921e41f4b71Sopenharmony_ci 922e41f4b71Sopenharmony_ciAn examination of the code finds out that when a value is re-assigned to the data source **ChildList** through the **loadData** method of the **Ancestor** object. 923e41f4b71Sopenharmony_ci 924e41f4b71Sopenharmony_ci```typescript 925e41f4b71Sopenharmony_ci public loadData() { 926e41f4b71Sopenharmony_ci let tempList = [new Child(1), new Child(2), new Child(3), new Child(4), new Child(5)]; 927e41f4b71Sopenharmony_ci this.childList = tempList; 928e41f4b71Sopenharmony_ci } 929e41f4b71Sopenharmony_ci``` 930e41f4b71Sopenharmony_ci 931e41f4b71Sopenharmony_ciIn the **loadData** method, **tempList**, a temporary array of the Child type, is created, to which the member variable **ChildList** of the **Ancestor** object is pointed. However, value changes of the **tempList** array cannot be observed. In other words, its value changes do not cause UI re-renders. After the array is assigned to **childList**, the **ForEach** view is updated and the UI is re-rendered. When you click **X** again, however, the UI is not re-rendered to reflect the decrease in **childList**, because **childList** points to a new, unobservable **tempList**. 932e41f4b71Sopenharmony_ci 933e41f4b71Sopenharmony_ciYou may notice that **childList** is initialized in the same way when it is defined in **Page**. 934e41f4b71Sopenharmony_ci 935e41f4b71Sopenharmony_ci```typescript 936e41f4b71Sopenharmony_ci@State childList: ChildList = [new Child(1), new Child(2), new Child(3), new Child(4),new Child(5)]; 937e41f4b71Sopenharmony_ci@State ancestor: Ancestor = new Ancestor(this.childList) 938e41f4b71Sopenharmony_ci``` 939e41f4b71Sopenharmony_ci 940e41f4b71Sopenharmony_ciYet, **childList** there is observable, being decorated by @State. As such, while it is assigned an array of the Child[] type not decorated by @Observed, its value changes can cause UI re-renders. If the @State decorator is removed from **childList**, the data source is not reset and UI re-renders cannot be triggered by clicking the **X** button. 941e41f4b71Sopenharmony_ci 942e41f4b71Sopenharmony_ciIn summary, for the UI to be re-rendered properly upon value changes of class objects, these class objects must be observable. 943e41f4b71Sopenharmony_ci 944e41f4b71Sopenharmony_ci```typescript 945e41f4b71Sopenharmony_ci@Observed 946e41f4b71Sopenharmony_ciclass Child { 947e41f4b71Sopenharmony_ci count: number; 948e41f4b71Sopenharmony_ci constructor(count: number) { 949e41f4b71Sopenharmony_ci this.count = count 950e41f4b71Sopenharmony_ci } 951e41f4b71Sopenharmony_ci} 952e41f4b71Sopenharmony_ci@Observed 953e41f4b71Sopenharmony_ciclass ChildList extends Array<Child> { 954e41f4b71Sopenharmony_ci}; 955e41f4b71Sopenharmony_ci@Observed 956e41f4b71Sopenharmony_ciclass Ancestor { 957e41f4b71Sopenharmony_ci childList: ChildList; 958e41f4b71Sopenharmony_ci constructor(childList: ChildList) { 959e41f4b71Sopenharmony_ci this.childList = childList; 960e41f4b71Sopenharmony_ci } 961e41f4b71Sopenharmony_ci public loadData() { 962e41f4b71Sopenharmony_ci let tempList = new ChildList(); 963e41f4b71Sopenharmony_ci for (let i = 1; i < 6; i ++) { 964e41f4b71Sopenharmony_ci tempList.push(new Child(i)); 965e41f4b71Sopenharmony_ci } 966e41f4b71Sopenharmony_ci this.childList = tempList; 967e41f4b71Sopenharmony_ci } 968e41f4b71Sopenharmony_ci 969e41f4b71Sopenharmony_ci public clearData() { 970e41f4b71Sopenharmony_ci this.childList = [] 971e41f4b71Sopenharmony_ci } 972e41f4b71Sopenharmony_ci} 973e41f4b71Sopenharmony_ci@Component 974e41f4b71Sopenharmony_cistruct CompChild { 975e41f4b71Sopenharmony_ci @Link childList: ChildList; 976e41f4b71Sopenharmony_ci @ObjectLink child: Child; 977e41f4b71Sopenharmony_ci 978e41f4b71Sopenharmony_ci build() { 979e41f4b71Sopenharmony_ci Row() { 980e41f4b71Sopenharmony_ci Text(this.child.count+'') 981e41f4b71Sopenharmony_ci .height(70) 982e41f4b71Sopenharmony_ci .fontSize(20) 983e41f4b71Sopenharmony_ci .borderRadius({ 984e41f4b71Sopenharmony_ci topLeft: 6, 985e41f4b71Sopenharmony_ci topRight: 6 986e41f4b71Sopenharmony_ci }) 987e41f4b71Sopenharmony_ci .margin({left: 50}) 988e41f4b71Sopenharmony_ci Button('X') 989e41f4b71Sopenharmony_ci .backgroundColor(Color.Red) 990e41f4b71Sopenharmony_ci .onClick(()=>{ 991e41f4b71Sopenharmony_ci let index = this.childList.findIndex((item) => { 992e41f4b71Sopenharmony_ci return item.count === this.child.count 993e41f4b71Sopenharmony_ci }) 994e41f4b71Sopenharmony_ci if (index !== -1) { 995e41f4b71Sopenharmony_ci this.childList.splice(index, 1); 996e41f4b71Sopenharmony_ci } 997e41f4b71Sopenharmony_ci }) 998e41f4b71Sopenharmony_ci .margin({ 999e41f4b71Sopenharmony_ci left: 200, 1000e41f4b71Sopenharmony_ci right:30 1001e41f4b71Sopenharmony_ci }) 1002e41f4b71Sopenharmony_ci } 1003e41f4b71Sopenharmony_ci .margin({ 1004e41f4b71Sopenharmony_ci top:15, 1005e41f4b71Sopenharmony_ci left: 15, 1006e41f4b71Sopenharmony_ci right:10, 1007e41f4b71Sopenharmony_ci bottom:15 1008e41f4b71Sopenharmony_ci }) 1009e41f4b71Sopenharmony_ci .borderRadius(6) 1010e41f4b71Sopenharmony_ci .backgroundColor(Color.Grey) 1011e41f4b71Sopenharmony_ci } 1012e41f4b71Sopenharmony_ci} 1013e41f4b71Sopenharmony_ci@Component 1014e41f4b71Sopenharmony_cistruct CompList { 1015e41f4b71Sopenharmony_ci @ObjectLink@Watch('changeChildList') childList: ChildList; 1016e41f4b71Sopenharmony_ci 1017e41f4b71Sopenharmony_ci changeChildList() { 1018e41f4b71Sopenharmony_ci console.log('CompList ChildList change'); 1019e41f4b71Sopenharmony_ci } 1020e41f4b71Sopenharmony_ci 1021e41f4b71Sopenharmony_ci isRenderCompChild(index: number) : number { 1022e41f4b71Sopenharmony_ci console.log("Comp Child is render" + index); 1023e41f4b71Sopenharmony_ci return 1; 1024e41f4b71Sopenharmony_ci } 1025e41f4b71Sopenharmony_ci 1026e41f4b71Sopenharmony_ci build() { 1027e41f4b71Sopenharmony_ci Column() { 1028e41f4b71Sopenharmony_ci List() { 1029e41f4b71Sopenharmony_ci ForEach(this.childList, (item: Child, index) => { 1030e41f4b71Sopenharmony_ci ListItem() { 1031e41f4b71Sopenharmony_ci // in low version, Dev Eco may throw a warning 1032e41f4b71Sopenharmony_ci // But you can still build and run the code 1033e41f4b71Sopenharmony_ci CompChild({ 1034e41f4b71Sopenharmony_ci childList: this.childList, 1035e41f4b71Sopenharmony_ci child: item 1036e41f4b71Sopenharmony_ci }) 1037e41f4b71Sopenharmony_ci .opacity(this.isRenderCompChild(index)) 1038e41f4b71Sopenharmony_ci } 1039e41f4b71Sopenharmony_ci 1040e41f4b71Sopenharmony_ci }) 1041e41f4b71Sopenharmony_ci } 1042e41f4b71Sopenharmony_ci .height('70%') 1043e41f4b71Sopenharmony_ci } 1044e41f4b71Sopenharmony_ci } 1045e41f4b71Sopenharmony_ci} 1046e41f4b71Sopenharmony_ci@Component 1047e41f4b71Sopenharmony_cistruct CompAncestor { 1048e41f4b71Sopenharmony_ci @ObjectLink ancestor: Ancestor; 1049e41f4b71Sopenharmony_ci 1050e41f4b71Sopenharmony_ci build() { 1051e41f4b71Sopenharmony_ci Column() { 1052e41f4b71Sopenharmony_ci // in low version, Dev Eco may throw a warning 1053e41f4b71Sopenharmony_ci // But you can still build and run the code 1054e41f4b71Sopenharmony_ci CompList({ childList: this.ancestor.childList }) 1055e41f4b71Sopenharmony_ci Row() { 1056e41f4b71Sopenharmony_ci Button("Clear") 1057e41f4b71Sopenharmony_ci .onClick(() => { 1058e41f4b71Sopenharmony_ci this.ancestor.clearData() 1059e41f4b71Sopenharmony_ci }) 1060e41f4b71Sopenharmony_ci .width(100) 1061e41f4b71Sopenharmony_ci .margin({right: 50}) 1062e41f4b71Sopenharmony_ci Button("Recover") 1063e41f4b71Sopenharmony_ci .onClick(() => { 1064e41f4b71Sopenharmony_ci this.ancestor.loadData() 1065e41f4b71Sopenharmony_ci }) 1066e41f4b71Sopenharmony_ci .width(100) 1067e41f4b71Sopenharmony_ci } 1068e41f4b71Sopenharmony_ci } 1069e41f4b71Sopenharmony_ci } 1070e41f4b71Sopenharmony_ci} 1071e41f4b71Sopenharmony_ci@Entry 1072e41f4b71Sopenharmony_ci@Component 1073e41f4b71Sopenharmony_cistruct Page { 1074e41f4b71Sopenharmony_ci @State childList: ChildList = [new Child(1), new Child(2), new Child(3), new Child(4),new Child(5)]; 1075e41f4b71Sopenharmony_ci @State ancestor: Ancestor = new Ancestor(this.childList) 1076e41f4b71Sopenharmony_ci 1077e41f4b71Sopenharmony_ci build() { 1078e41f4b71Sopenharmony_ci Column() { 1079e41f4b71Sopenharmony_ci // in low version, Dev Eco may throw a warning 1080e41f4b71Sopenharmony_ci // But you can still build and run the code 1081e41f4b71Sopenharmony_ci CompAncestor({ ancestor: this.ancestor}) 1082e41f4b71Sopenharmony_ci } 1083e41f4b71Sopenharmony_ci } 1084e41f4b71Sopenharmony_ci} 1085e41f4b71Sopenharmony_ci``` 1086e41f4b71Sopenharmony_ci 1087e41f4b71Sopenharmony_ciBelow you can see how the preceding code snippet works. 1088e41f4b71Sopenharmony_ci 1089e41f4b71Sopenharmony_ci 1090e41f4b71Sopenharmony_ci 1091e41f4b71Sopenharmony_ciThe core of optimization is to change **tempList** of the Child[] type to an observable **ChildList** class. 1092e41f4b71Sopenharmony_ci 1093e41f4b71Sopenharmony_ci```typescript 1094e41f4b71Sopenharmony_cipublic loadData() { 1095e41f4b71Sopenharmony_ci let tempList = new ChildList(); 1096e41f4b71Sopenharmony_ci for (let i = 1; i < 6; i ++) { 1097e41f4b71Sopenharmony_ci tempList.push(new Child(i)); 1098e41f4b71Sopenharmony_ci } 1099e41f4b71Sopenharmony_ci this.childList = tempList; 1100e41f4b71Sopenharmony_ci } 1101e41f4b71Sopenharmony_ci``` 1102e41f4b71Sopenharmony_ci 1103e41f4b71Sopenharmony_ciIn the preceding code, the ChildList type is decorated by @Observed when defined, allowing the **tempList** object created using **new** to be observed. As such, when you click **X** to delete an item, this change to **childList** is observed, the **ForEach** view updated, and the UI re-rendered. 1104e41f4b71Sopenharmony_ci 1105e41f4b71Sopenharmony_ci## Properly Using ForEach and LazyForEach 1106e41f4b71Sopenharmony_ci 1107e41f4b71Sopenharmony_ci### Minimizing the Use of LazyForEach in UI Updating 1108e41f4b71Sopenharmony_ci 1109e41f4b71Sopenharmony_ci[LazyForEach](arkts-rendering-control-lazyforeach.md) often works hand in hand with state variables. 1110e41f4b71Sopenharmony_ci 1111e41f4b71Sopenharmony_ci```typescript 1112e41f4b71Sopenharmony_ciclass BasicDataSource implements IDataSource { 1113e41f4b71Sopenharmony_ci private listeners: DataChangeListener[] = []; 1114e41f4b71Sopenharmony_ci private originDataArray: StringData[] = []; 1115e41f4b71Sopenharmony_ci 1116e41f4b71Sopenharmony_ci public totalCount(): number { 1117e41f4b71Sopenharmony_ci return 0; 1118e41f4b71Sopenharmony_ci } 1119e41f4b71Sopenharmony_ci 1120e41f4b71Sopenharmony_ci public getData(index: number): StringData { 1121e41f4b71Sopenharmony_ci return this.originDataArray[index]; 1122e41f4b71Sopenharmony_ci } 1123e41f4b71Sopenharmony_ci 1124e41f4b71Sopenharmony_ci registerDataChangeListener(listener: DataChangeListener): void { 1125e41f4b71Sopenharmony_ci if (this.listeners.indexOf(listener) < 0) { 1126e41f4b71Sopenharmony_ci console.info('add listener'); 1127e41f4b71Sopenharmony_ci this.listeners.push(listener); 1128e41f4b71Sopenharmony_ci } 1129e41f4b71Sopenharmony_ci } 1130e41f4b71Sopenharmony_ci 1131e41f4b71Sopenharmony_ci unregisterDataChangeListener(listener: DataChangeListener): void { 1132e41f4b71Sopenharmony_ci const pos = this.listeners.indexOf(listener); 1133e41f4b71Sopenharmony_ci if (pos >= 0) { 1134e41f4b71Sopenharmony_ci console.info('remove listener'); 1135e41f4b71Sopenharmony_ci this.listeners.splice(pos, 1); 1136e41f4b71Sopenharmony_ci } 1137e41f4b71Sopenharmony_ci } 1138e41f4b71Sopenharmony_ci 1139e41f4b71Sopenharmony_ci notifyDataReload(): void { 1140e41f4b71Sopenharmony_ci this.listeners.forEach(listener => { 1141e41f4b71Sopenharmony_ci listener.onDataReloaded(); 1142e41f4b71Sopenharmony_ci }) 1143e41f4b71Sopenharmony_ci } 1144e41f4b71Sopenharmony_ci 1145e41f4b71Sopenharmony_ci notifyDataAdd(index: number): void { 1146e41f4b71Sopenharmony_ci this.listeners.forEach(listener => { 1147e41f4b71Sopenharmony_ci listener.onDataAdd(index); 1148e41f4b71Sopenharmony_ci }) 1149e41f4b71Sopenharmony_ci } 1150e41f4b71Sopenharmony_ci 1151e41f4b71Sopenharmony_ci notifyDataChange(index: number): void { 1152e41f4b71Sopenharmony_ci this.listeners.forEach(listener => { 1153e41f4b71Sopenharmony_ci listener.onDataChange(index); 1154e41f4b71Sopenharmony_ci }) 1155e41f4b71Sopenharmony_ci } 1156e41f4b71Sopenharmony_ci 1157e41f4b71Sopenharmony_ci notifyDataDelete(index: number): void { 1158e41f4b71Sopenharmony_ci this.listeners.forEach(listener => { 1159e41f4b71Sopenharmony_ci listener.onDataDelete(index); 1160e41f4b71Sopenharmony_ci }) 1161e41f4b71Sopenharmony_ci } 1162e41f4b71Sopenharmony_ci 1163e41f4b71Sopenharmony_ci notifyDataMove(from: number, to: number): void { 1164e41f4b71Sopenharmony_ci this.listeners.forEach(listener => { 1165e41f4b71Sopenharmony_ci listener.onDataMove(from, to); 1166e41f4b71Sopenharmony_ci }) 1167e41f4b71Sopenharmony_ci } 1168e41f4b71Sopenharmony_ci} 1169e41f4b71Sopenharmony_ci 1170e41f4b71Sopenharmony_ciclass MyDataSource extends BasicDataSource { 1171e41f4b71Sopenharmony_ci private dataArray: StringData[] = []; 1172e41f4b71Sopenharmony_ci 1173e41f4b71Sopenharmony_ci public totalCount(): number { 1174e41f4b71Sopenharmony_ci return this.dataArray.length; 1175e41f4b71Sopenharmony_ci } 1176e41f4b71Sopenharmony_ci 1177e41f4b71Sopenharmony_ci public getData(index: number): StringData { 1178e41f4b71Sopenharmony_ci return this.dataArray[index]; 1179e41f4b71Sopenharmony_ci } 1180e41f4b71Sopenharmony_ci 1181e41f4b71Sopenharmony_ci public addData(index: number, data: StringData): void { 1182e41f4b71Sopenharmony_ci this.dataArray.splice(index, 0, data); 1183e41f4b71Sopenharmony_ci this.notifyDataAdd(index); 1184e41f4b71Sopenharmony_ci } 1185e41f4b71Sopenharmony_ci 1186e41f4b71Sopenharmony_ci public pushData(data: StringData): void { 1187e41f4b71Sopenharmony_ci this.dataArray.push(data); 1188e41f4b71Sopenharmony_ci this.notifyDataAdd(this.dataArray.length - 1); 1189e41f4b71Sopenharmony_ci } 1190e41f4b71Sopenharmony_ci 1191e41f4b71Sopenharmony_ci public reloadData(): void { 1192e41f4b71Sopenharmony_ci this.notifyDataReload(); 1193e41f4b71Sopenharmony_ci } 1194e41f4b71Sopenharmony_ci} 1195e41f4b71Sopenharmony_ci 1196e41f4b71Sopenharmony_ciclass StringData { 1197e41f4b71Sopenharmony_ci message: string; 1198e41f4b71Sopenharmony_ci imgSrc: Resource; 1199e41f4b71Sopenharmony_ci constructor(message: string, imgSrc: Resource) { 1200e41f4b71Sopenharmony_ci this.message = message; 1201e41f4b71Sopenharmony_ci this.imgSrc = imgSrc; 1202e41f4b71Sopenharmony_ci } 1203e41f4b71Sopenharmony_ci} 1204e41f4b71Sopenharmony_ci 1205e41f4b71Sopenharmony_ci@Entry 1206e41f4b71Sopenharmony_ci@Component 1207e41f4b71Sopenharmony_cistruct MyComponent { 1208e41f4b71Sopenharmony_ci private data: MyDataSource = new MyDataSource(); 1209e41f4b71Sopenharmony_ci 1210e41f4b71Sopenharmony_ci aboutToAppear() { 1211e41f4b71Sopenharmony_ci for (let i = 0; i <= 9; i++) { 1212e41f4b71Sopenharmony_ci this.data.pushData(new StringData(`Click to add ${i}`, $r('app.media.icon'))); // Use app.media.app_icon since API version 12. 1213e41f4b71Sopenharmony_ci } 1214e41f4b71Sopenharmony_ci } 1215e41f4b71Sopenharmony_ci 1216e41f4b71Sopenharmony_ci build() { 1217e41f4b71Sopenharmony_ci List({ space: 3 }) { 1218e41f4b71Sopenharmony_ci LazyForEach(this.data, (item: StringData, index: number) => { 1219e41f4b71Sopenharmony_ci ListItem() { 1220e41f4b71Sopenharmony_ci Column() { 1221e41f4b71Sopenharmony_ci Text(item.message).fontSize(20) 1222e41f4b71Sopenharmony_ci .onAppear(() => { 1223e41f4b71Sopenharmony_ci console.info("text appear:" + item.message); 1224e41f4b71Sopenharmony_ci }) 1225e41f4b71Sopenharmony_ci Image(item.imgSrc) 1226e41f4b71Sopenharmony_ci .width(100) 1227e41f4b71Sopenharmony_ci .height(100) 1228e41f4b71Sopenharmony_ci .onAppear(() => { 1229e41f4b71Sopenharmony_ci console.info("image appear"); 1230e41f4b71Sopenharmony_ci }) 1231e41f4b71Sopenharmony_ci }.margin({ left: 10, right: 10 }) 1232e41f4b71Sopenharmony_ci } 1233e41f4b71Sopenharmony_ci .onClick(() => { 1234e41f4b71Sopenharmony_ci item.message += '0'; 1235e41f4b71Sopenharmony_ci this.data.reloadData(); 1236e41f4b71Sopenharmony_ci }) 1237e41f4b71Sopenharmony_ci }, (item: StringData, index: number) => JSON.stringify(item)) 1238e41f4b71Sopenharmony_ci }.cachedCount(5) 1239e41f4b71Sopenharmony_ci } 1240e41f4b71Sopenharmony_ci} 1241e41f4b71Sopenharmony_ci``` 1242e41f4b71Sopenharmony_ci 1243e41f4b71Sopenharmony_ciBelow you can see how the preceding code snippet works. 1244e41f4b71Sopenharmony_ci 1245e41f4b71Sopenharmony_ci 1246e41f4b71Sopenharmony_ci 1247e41f4b71Sopenharmony_ciIn this example, after you click to change **message**, the image flickers, and the onAppear log is generated for the image, indicating that the component is rebuilt. After **message** is changed, the key of the corresponding list item in **LazyForEach** changes. As a result, **LazyForEach** rebuilds the list item when executing **reloadData**. Though the **Text** component only has its content changed, it is rebuilt, not updated. The **Image** component under the list item is also rebuilt along with the list item, even though its content remains unchanged. 1248e41f4b71Sopenharmony_ci 1249e41f4b71Sopenharmony_ciWhile both **LazyForEach** and state variables can trigger UI re-renders, their performance overheads are different. **LazyForEach** leads to component rebuilds and higher performance overheads, especially when there is a considerable number of components. By contrast, the use of state variables allows you to keep the update scope within the closely related components. In light of this, it is recommended that you use state variables to trigger component updates in **LazyForEach**, which requires custom components. 1250e41f4b71Sopenharmony_ci 1251e41f4b71Sopenharmony_ci```typescript 1252e41f4b71Sopenharmony_ciclass BasicDataSource implements IDataSource { 1253e41f4b71Sopenharmony_ci private listeners: DataChangeListener[] = []; 1254e41f4b71Sopenharmony_ci private originDataArray: StringData[] = []; 1255e41f4b71Sopenharmony_ci 1256e41f4b71Sopenharmony_ci public totalCount(): number { 1257e41f4b71Sopenharmony_ci return 0; 1258e41f4b71Sopenharmony_ci } 1259e41f4b71Sopenharmony_ci 1260e41f4b71Sopenharmony_ci public getData(index: number): StringData { 1261e41f4b71Sopenharmony_ci return this.originDataArray[index]; 1262e41f4b71Sopenharmony_ci } 1263e41f4b71Sopenharmony_ci 1264e41f4b71Sopenharmony_ci registerDataChangeListener(listener: DataChangeListener): void { 1265e41f4b71Sopenharmony_ci if (this.listeners.indexOf(listener) < 0) { 1266e41f4b71Sopenharmony_ci console.info('add listener'); 1267e41f4b71Sopenharmony_ci this.listeners.push(listener); 1268e41f4b71Sopenharmony_ci } 1269e41f4b71Sopenharmony_ci } 1270e41f4b71Sopenharmony_ci 1271e41f4b71Sopenharmony_ci unregisterDataChangeListener(listener: DataChangeListener): void { 1272e41f4b71Sopenharmony_ci const pos = this.listeners.indexOf(listener); 1273e41f4b71Sopenharmony_ci if (pos >= 0) { 1274e41f4b71Sopenharmony_ci console.info('remove listener'); 1275e41f4b71Sopenharmony_ci this.listeners.splice(pos, 1); 1276e41f4b71Sopenharmony_ci } 1277e41f4b71Sopenharmony_ci } 1278e41f4b71Sopenharmony_ci 1279e41f4b71Sopenharmony_ci notifyDataReload(): void { 1280e41f4b71Sopenharmony_ci this.listeners.forEach(listener => { 1281e41f4b71Sopenharmony_ci listener.onDataReloaded(); 1282e41f4b71Sopenharmony_ci }) 1283e41f4b71Sopenharmony_ci } 1284e41f4b71Sopenharmony_ci 1285e41f4b71Sopenharmony_ci notifyDataAdd(index: number): void { 1286e41f4b71Sopenharmony_ci this.listeners.forEach(listener => { 1287e41f4b71Sopenharmony_ci listener.onDataAdd(index); 1288e41f4b71Sopenharmony_ci }) 1289e41f4b71Sopenharmony_ci } 1290e41f4b71Sopenharmony_ci 1291e41f4b71Sopenharmony_ci notifyDataChange(index: number): void { 1292e41f4b71Sopenharmony_ci this.listeners.forEach(listener => { 1293e41f4b71Sopenharmony_ci listener.onDataChange(index); 1294e41f4b71Sopenharmony_ci }) 1295e41f4b71Sopenharmony_ci } 1296e41f4b71Sopenharmony_ci 1297e41f4b71Sopenharmony_ci notifyDataDelete(index: number): void { 1298e41f4b71Sopenharmony_ci this.listeners.forEach(listener => { 1299e41f4b71Sopenharmony_ci listener.onDataDelete(index); 1300e41f4b71Sopenharmony_ci }) 1301e41f4b71Sopenharmony_ci } 1302e41f4b71Sopenharmony_ci 1303e41f4b71Sopenharmony_ci notifyDataMove(from: number, to: number): void { 1304e41f4b71Sopenharmony_ci this.listeners.forEach(listener => { 1305e41f4b71Sopenharmony_ci listener.onDataMove(from, to); 1306e41f4b71Sopenharmony_ci }) 1307e41f4b71Sopenharmony_ci } 1308e41f4b71Sopenharmony_ci} 1309e41f4b71Sopenharmony_ci 1310e41f4b71Sopenharmony_ciclass MyDataSource extends BasicDataSource { 1311e41f4b71Sopenharmony_ci private dataArray: StringData[] = []; 1312e41f4b71Sopenharmony_ci 1313e41f4b71Sopenharmony_ci public totalCount(): number { 1314e41f4b71Sopenharmony_ci return this.dataArray.length; 1315e41f4b71Sopenharmony_ci } 1316e41f4b71Sopenharmony_ci 1317e41f4b71Sopenharmony_ci public getData(index: number): StringData { 1318e41f4b71Sopenharmony_ci return this.dataArray[index]; 1319e41f4b71Sopenharmony_ci } 1320e41f4b71Sopenharmony_ci 1321e41f4b71Sopenharmony_ci public addData(index: number, data: StringData): void { 1322e41f4b71Sopenharmony_ci this.dataArray.splice(index, 0, data); 1323e41f4b71Sopenharmony_ci this.notifyDataAdd(index); 1324e41f4b71Sopenharmony_ci } 1325e41f4b71Sopenharmony_ci 1326e41f4b71Sopenharmony_ci public pushData(data: StringData): void { 1327e41f4b71Sopenharmony_ci this.dataArray.push(data); 1328e41f4b71Sopenharmony_ci this.notifyDataAdd(this.dataArray.length - 1); 1329e41f4b71Sopenharmony_ci } 1330e41f4b71Sopenharmony_ci} 1331e41f4b71Sopenharmony_ci 1332e41f4b71Sopenharmony_ci@Observed 1333e41f4b71Sopenharmony_ciclass StringData { 1334e41f4b71Sopenharmony_ci @Track message: string; 1335e41f4b71Sopenharmony_ci @Track imgSrc: Resource; 1336e41f4b71Sopenharmony_ci constructor(message: string, imgSrc: Resource) { 1337e41f4b71Sopenharmony_ci this.message = message; 1338e41f4b71Sopenharmony_ci this.imgSrc = imgSrc; 1339e41f4b71Sopenharmony_ci } 1340e41f4b71Sopenharmony_ci} 1341e41f4b71Sopenharmony_ci 1342e41f4b71Sopenharmony_ci@Entry 1343e41f4b71Sopenharmony_ci@Component 1344e41f4b71Sopenharmony_cistruct MyComponent { 1345e41f4b71Sopenharmony_ci @State data: MyDataSource = new MyDataSource(); 1346e41f4b71Sopenharmony_ci 1347e41f4b71Sopenharmony_ci aboutToAppear() { 1348e41f4b71Sopenharmony_ci for (let i = 0; i <= 9; i++) { 1349e41f4b71Sopenharmony_ci this.data.pushData(new StringData(`Click to add ${i}`, $r('app.media.icon'))); // Use app.media.app_icon since API version 12. 1350e41f4b71Sopenharmony_ci } 1351e41f4b71Sopenharmony_ci } 1352e41f4b71Sopenharmony_ci 1353e41f4b71Sopenharmony_ci build() { 1354e41f4b71Sopenharmony_ci List({ space: 3 }) { 1355e41f4b71Sopenharmony_ci LazyForEach(this.data, (item: StringData, index: number) => { 1356e41f4b71Sopenharmony_ci ListItem() { 1357e41f4b71Sopenharmony_ci // in low version, Dev Eco may throw a warning 1358e41f4b71Sopenharmony_ci // But you can still build and run the code 1359e41f4b71Sopenharmony_ci ChildComponent({data: item}) 1360e41f4b71Sopenharmony_ci } 1361e41f4b71Sopenharmony_ci .onClick(() => { 1362e41f4b71Sopenharmony_ci item.message += '0'; 1363e41f4b71Sopenharmony_ci }) 1364e41f4b71Sopenharmony_ci }, (item: StringData, index: number) => index.toString()) 1365e41f4b71Sopenharmony_ci }.cachedCount(5) 1366e41f4b71Sopenharmony_ci } 1367e41f4b71Sopenharmony_ci} 1368e41f4b71Sopenharmony_ci 1369e41f4b71Sopenharmony_ci@Component 1370e41f4b71Sopenharmony_cistruct ChildComponent { 1371e41f4b71Sopenharmony_ci @ObjectLink data: StringData 1372e41f4b71Sopenharmony_ci build() { 1373e41f4b71Sopenharmony_ci Column() { 1374e41f4b71Sopenharmony_ci Text(this.data.message).fontSize(20) 1375e41f4b71Sopenharmony_ci .onAppear(() => { 1376e41f4b71Sopenharmony_ci console.info("text appear:" + this.data.message) 1377e41f4b71Sopenharmony_ci }) 1378e41f4b71Sopenharmony_ci Image(this.data.imgSrc) 1379e41f4b71Sopenharmony_ci .width(100) 1380e41f4b71Sopenharmony_ci .height(100) 1381e41f4b71Sopenharmony_ci }.margin({ left: 10, right: 10 }) 1382e41f4b71Sopenharmony_ci } 1383e41f4b71Sopenharmony_ci} 1384e41f4b71Sopenharmony_ci``` 1385e41f4b71Sopenharmony_ci 1386e41f4b71Sopenharmony_ciBelow you can see how the preceding code snippet works. 1387e41f4b71Sopenharmony_ci 1388e41f4b71Sopenharmony_ci 1389e41f4b71Sopenharmony_ci 1390e41f4b71Sopenharmony_ciIn this example, the UI is re-rendered properly: The image does not flicker, and no log is generated, which indicates that the **Text** and **Image** components are not rebuilt. 1391e41f4b71Sopenharmony_ci 1392e41f4b71Sopenharmony_ciThis is thanks to introduction of custom components, where state variables are directly changed through @Observed and @ObjectLink, instead of through **LazyForEach**. Decorate the **message** and **imgSrc** properties of the **StringData** type with [@Track](arkts-track.md) to further narrow down the render scope to the specified **Text** component. 1393e41f4b71Sopenharmony_ci 1394e41f4b71Sopenharmony_ci### Using Custom Components to Match Object Arrays in ForEach 1395e41f4b71Sopenharmony_ci 1396e41f4b71Sopenharmony_ciFrequently seen in applications, the combination of object arrays and [ForEach](arkts-rendering-control-foreach.md) requires special attentions. Inappropriate use may cause UI re-render issues. 1397e41f4b71Sopenharmony_ci 1398e41f4b71Sopenharmony_ci```typescript 1399e41f4b71Sopenharmony_ci@Observed 1400e41f4b71Sopenharmony_ciclass StyleList extends Array<TextStyle> { 1401e41f4b71Sopenharmony_ci}; 1402e41f4b71Sopenharmony_ci@Observed 1403e41f4b71Sopenharmony_ciclass TextStyle { 1404e41f4b71Sopenharmony_ci fontSize: number; 1405e41f4b71Sopenharmony_ci 1406e41f4b71Sopenharmony_ci constructor(fontSize: number) { 1407e41f4b71Sopenharmony_ci this.fontSize = fontSize; 1408e41f4b71Sopenharmony_ci } 1409e41f4b71Sopenharmony_ci} 1410e41f4b71Sopenharmony_ci@Entry 1411e41f4b71Sopenharmony_ci@Component 1412e41f4b71Sopenharmony_cistruct Page { 1413e41f4b71Sopenharmony_ci @State styleList: StyleList = new StyleList(); 1414e41f4b71Sopenharmony_ci aboutToAppear() { 1415e41f4b71Sopenharmony_ci for (let i = 15; i < 50; i++) 1416e41f4b71Sopenharmony_ci this.styleList.push(new TextStyle(i)); 1417e41f4b71Sopenharmony_ci } 1418e41f4b71Sopenharmony_ci build() { 1419e41f4b71Sopenharmony_ci Column() { 1420e41f4b71Sopenharmony_ci Text("Font Size List") 1421e41f4b71Sopenharmony_ci .fontSize(50) 1422e41f4b71Sopenharmony_ci .onClick(() => { 1423e41f4b71Sopenharmony_ci for (let i = 0; i < this.styleList.length; i++) { 1424e41f4b71Sopenharmony_ci this.styleList[i].fontSize++; 1425e41f4b71Sopenharmony_ci } 1426e41f4b71Sopenharmony_ci console.log("change font size"); 1427e41f4b71Sopenharmony_ci }) 1428e41f4b71Sopenharmony_ci List() { 1429e41f4b71Sopenharmony_ci ForEach(this.styleList, (item: TextStyle) => { 1430e41f4b71Sopenharmony_ci ListItem() { 1431e41f4b71Sopenharmony_ci Text("Hello World") 1432e41f4b71Sopenharmony_ci .fontSize(item.fontSize) 1433e41f4b71Sopenharmony_ci } 1434e41f4b71Sopenharmony_ci }) 1435e41f4b71Sopenharmony_ci } 1436e41f4b71Sopenharmony_ci } 1437e41f4b71Sopenharmony_ci } 1438e41f4b71Sopenharmony_ci} 1439e41f4b71Sopenharmony_ci``` 1440e41f4b71Sopenharmony_ci 1441e41f4b71Sopenharmony_ciBelow you can see how the preceding code snippet works. 1442e41f4b71Sopenharmony_ci 1443e41f4b71Sopenharmony_ci 1444e41f4b71Sopenharmony_ci 1445e41f4b71Sopenharmony_ciThe items generated in **ForEach** are constants. This means that their value changes do not trigger UI re-renders. In this example, though an item is changed upon a click, as indicated by the "change font size" log, the UI is not updated as expected. To fix this issue, you need to use custom components with @ObjectLink. 1446e41f4b71Sopenharmony_ci 1447e41f4b71Sopenharmony_ci```typescript 1448e41f4b71Sopenharmony_ci@Observed 1449e41f4b71Sopenharmony_ciclass StyleList extends Array<TextStyle> { 1450e41f4b71Sopenharmony_ci}; 1451e41f4b71Sopenharmony_ci@Observed 1452e41f4b71Sopenharmony_ciclass TextStyle { 1453e41f4b71Sopenharmony_ci fontSize: number; 1454e41f4b71Sopenharmony_ci 1455e41f4b71Sopenharmony_ci constructor(fontSize: number) { 1456e41f4b71Sopenharmony_ci this.fontSize = fontSize; 1457e41f4b71Sopenharmony_ci } 1458e41f4b71Sopenharmony_ci} 1459e41f4b71Sopenharmony_ci@Component 1460e41f4b71Sopenharmony_cistruct TextComponent { 1461e41f4b71Sopenharmony_ci @ObjectLink textStyle: TextStyle; 1462e41f4b71Sopenharmony_ci build() { 1463e41f4b71Sopenharmony_ci Text("Hello World") 1464e41f4b71Sopenharmony_ci .fontSize(this.textStyle.fontSize) 1465e41f4b71Sopenharmony_ci } 1466e41f4b71Sopenharmony_ci} 1467e41f4b71Sopenharmony_ci@Entry 1468e41f4b71Sopenharmony_ci@Component 1469e41f4b71Sopenharmony_cistruct Page { 1470e41f4b71Sopenharmony_ci @State styleList: StyleList = new StyleList(); 1471e41f4b71Sopenharmony_ci aboutToAppear() { 1472e41f4b71Sopenharmony_ci for (let i = 15; i < 50; i++) 1473e41f4b71Sopenharmony_ci this.styleList.push(new TextStyle(i)); 1474e41f4b71Sopenharmony_ci } 1475e41f4b71Sopenharmony_ci build() { 1476e41f4b71Sopenharmony_ci Column() { 1477e41f4b71Sopenharmony_ci Text("Font Size List") 1478e41f4b71Sopenharmony_ci .fontSize(50) 1479e41f4b71Sopenharmony_ci .onClick(() => { 1480e41f4b71Sopenharmony_ci for (let i = 0; i < this.styleList.length; i++) { 1481e41f4b71Sopenharmony_ci this.styleList[i].fontSize++; 1482e41f4b71Sopenharmony_ci } 1483e41f4b71Sopenharmony_ci console.log("change font size"); 1484e41f4b71Sopenharmony_ci }) 1485e41f4b71Sopenharmony_ci List() { 1486e41f4b71Sopenharmony_ci ForEach(this.styleList, (item: TextStyle) => { 1487e41f4b71Sopenharmony_ci ListItem() { 1488e41f4b71Sopenharmony_ci // in low version, Dev Eco may throw a warning 1489e41f4b71Sopenharmony_ci // But you can still build and run the code 1490e41f4b71Sopenharmony_ci TextComponent({ textStyle: item}) 1491e41f4b71Sopenharmony_ci } 1492e41f4b71Sopenharmony_ci }) 1493e41f4b71Sopenharmony_ci } 1494e41f4b71Sopenharmony_ci } 1495e41f4b71Sopenharmony_ci } 1496e41f4b71Sopenharmony_ci} 1497e41f4b71Sopenharmony_ci``` 1498e41f4b71Sopenharmony_ci 1499e41f4b71Sopenharmony_ciBelow you can see how the preceding code snippet works. 1500e41f4b71Sopenharmony_ci 1501e41f4b71Sopenharmony_ci 1502e41f4b71Sopenharmony_ci 1503e41f4b71Sopenharmony_ciWhen @ObjectLink is used to accept the input item, the **textStyle** variable in the **TextComponent** component can be observed. For @ObjectLink, parameters are passed by reference. Therefore, when the value of **fontSize** in **styleList** is changed in the parent component, this update is properly observed and synced to the corresponding list item in **ForEach**, leading to UI re-rendering. 1504e41f4b71Sopenharmony_ci 1505e41f4b71Sopenharmony_ciThis is a practical mode of using state management for UI re-rendering. 1506e41f4b71Sopenharmony_ci 1507e41f4b71Sopenharmony_ci<!--no_check--> 1508