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![properly-use-state-management-to-develope-1](figures/properly-use-state-management-to-develope-1.gif)
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![properly-use-state-management-to-develope-2](figures/properly-use-state-management-to-develope-2.gif)
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![properly-use-state-management-to-develope-3](figures/properly-use-state-management-to-develope-3.gif)
372e41f4b71Sopenharmony_ci
373e41f4b71Sopenharmony_ciClick the **Move** button before optimization. The duration for updating dirty nodes is as follows.
374e41f4b71Sopenharmony_ci
375e41f4b71Sopenharmony_ci![img](figures/properly-use-state-management-to-develope-11.PNG)
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.![properly-use-state-management-to-develope-4](figures/properly-use-state-management-to-develope-4.gif)
600e41f4b71Sopenharmony_ci
601e41f4b71Sopenharmony_ciClick the **Move** button after optimization. The duration for updating dirty nodes is as follows.
602e41f4b71Sopenharmony_ci
603e41f4b71Sopenharmony_ci![img](figures/properly-use-state-management-to-develope-12.PNG)
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![properly-use-state-management-to-develope-5](figures/properly-use-state-management-to-develope-5.gif)
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![properly-use-state-management-to-develope-6](figures/properly-use-state-management-to-develope-6.gif)
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![properly-use-state-management-to-develope-7](figures/properly-use-state-management-to-develope-7.gif)
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![properly-use-state-management-to-develope-8](figures/properly-use-state-management-to-develope-8.gif)
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![properly-use-state-management-to-develope-9](figures/properly-use-state-management-to-develope-9.gif)
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![properly-use-state-management-to-develope-10](figures/properly-use-state-management-to-develope-10.gif)
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