1e41f4b71Sopenharmony_ci# Precisely Controlling Render Scope
2e41f4b71Sopenharmony_ci
3e41f4b71Sopenharmony_ciIn development of complex pages, precisely controlling the component render scope is especially important to speed up applications.
4e41f4b71Sopenharmony_ci
5e41f4b71Sopenharmony_ciThis document exemplifies why and how the component render scope may be precisely controlled. For starters, you need to understand the re-render mechanism with state management.
6e41f4b71Sopenharmony_ci
7e41f4b71Sopenharmony_ci```ts
8e41f4b71Sopenharmony_ci@Observed
9e41f4b71Sopenharmony_ciclass ClassA {
10e41f4b71Sopenharmony_ci  prop1: number = 0;
11e41f4b71Sopenharmony_ci  prop2: string = "This is Prop2";
12e41f4b71Sopenharmony_ci}
13e41f4b71Sopenharmony_ci@Component
14e41f4b71Sopenharmony_cistruct CompA {
15e41f4b71Sopenharmony_ci  @ObjectLink a: ClassA;
16e41f4b71Sopenharmony_ci  private sizeFont: number = 30; // the private variable does not invoke rendering
17e41f4b71Sopenharmony_ci  private isRenderText() : number {
18e41f4b71Sopenharmony_ci    this.sizeFont++; // the change of sizeFont will not invoke rendering, but showing that the function is called
19e41f4b71Sopenharmony_ci    console.log("Text prop2 is rendered");
20e41f4b71Sopenharmony_ci    return this.sizeFont;
21e41f4b71Sopenharmony_ci  }
22e41f4b71Sopenharmony_ci  build() {
23e41f4b71Sopenharmony_ci    Column() {
24e41f4b71Sopenharmony_ci      Text(this.a.prop2) // when this.a.prop2 changes, it will invoke Text rerendering
25e41f4b71Sopenharmony_ci        .fontSize(this.isRenderText()) // If the <Text> renders, the function isRenderText will be called.
26e41f4b71Sopenharmony_ci    }
27e41f4b71Sopenharmony_ci  }
28e41f4b71Sopenharmony_ci}
29e41f4b71Sopenharmony_ci@Entry
30e41f4b71Sopenharmony_ci@Component
31e41f4b71Sopenharmony_cistruct Page {
32e41f4b71Sopenharmony_ci  @State a: ClassA = new ClassA();
33e41f4b71Sopenharmony_ci  build() {
34e41f4b71Sopenharmony_ci    Row() {
35e41f4b71Sopenharmony_ci      Column() {
36e41f4b71Sopenharmony_ci        Text("Prop1: " + this.a.prop1)
37e41f4b71Sopenharmony_ci          .fontSize(50)
38e41f4b71Sopenharmony_ci          .margin({ bottom: 20 })
39e41f4b71Sopenharmony_ci        CompA({a: this.a})
40e41f4b71Sopenharmony_ci        Button("Change prop1")
41e41f4b71Sopenharmony_ci          .width(200)
42e41f4b71Sopenharmony_ci          .margin({ top: 20 })
43e41f4b71Sopenharmony_ci          .onClick(() => {
44e41f4b71Sopenharmony_ci            this.a.prop1 = this.a.prop1 + 1 ;
45e41f4b71Sopenharmony_ci          })
46e41f4b71Sopenharmony_ci      }
47e41f4b71Sopenharmony_ci      .width('100%')
48e41f4b71Sopenharmony_ci    }
49e41f4b71Sopenharmony_ci    .width('100%')
50e41f4b71Sopenharmony_ci    .height('100%')
51e41f4b71Sopenharmony_ci  }
52e41f4b71Sopenharmony_ci}
53e41f4b71Sopenharmony_ci```
54e41f4b71Sopenharmony_ci
55e41f4b71Sopenharmony_ciIn the preceding example, when the value of **prop1** changes at the click of the button, although the components in **CompA** do not use **prop1**, you can still observe that the **\<Text>** component associated with **prop2** is re-rendered – reflected by the component's enlarged font size and the console log of "Text prop2 is rendered." This indicates that, when a property (**prop1** in this example) of an @Observed decorated class object is changed, all components associated with any property of this object are re-rendered at once, even though these components may not directly use the changed property (i.e., the **\<Text>** component using **prop** in this example). In this case, invisible, redundant re-renders occur. When a large number of components are involved in redundant re-renders, the render performance is greatly affected.
56e41f4b71Sopenharmony_ci
57e41f4b71Sopenharmony_ciThe following figure shows the code running.
58e41f4b71Sopenharmony_ci
59e41f4b71Sopenharmony_ci![precisely-control-render-scope-01.gif](figures/precisely-control-render-scope-01.gif)
60e41f4b71Sopenharmony_ci
61e41f4b71Sopenharmony_ciThe following is a typical example of redundant re-renders.
62e41f4b71Sopenharmony_ci
63e41f4b71Sopenharmony_ci```ts
64e41f4b71Sopenharmony_ci@Observed
65e41f4b71Sopenharmony_ciclass UIStyle {
66e41f4b71Sopenharmony_ci  translateX: number = 0;
67e41f4b71Sopenharmony_ci  translateY: number = 0;
68e41f4b71Sopenharmony_ci  scaleX: number = 0.3;
69e41f4b71Sopenharmony_ci  scaleY: number = 0.3;
70e41f4b71Sopenharmony_ci  width: number = 336;
71e41f4b71Sopenharmony_ci  height: number = 178;
72e41f4b71Sopenharmony_ci  posX: number = 10;
73e41f4b71Sopenharmony_ci  posY: number = 50;
74e41f4b71Sopenharmony_ci  alpha: number = 0.5;
75e41f4b71Sopenharmony_ci  borderRadius: number = 24;
76e41f4b71Sopenharmony_ci  imageWidth: number = 78;
77e41f4b71Sopenharmony_ci  imageHeight: number = 78;
78e41f4b71Sopenharmony_ci  translateImageX: number = 0;
79e41f4b71Sopenharmony_ci  translateImageY: number = 0;
80e41f4b71Sopenharmony_ci  fontSize: number = 20;
81e41f4b71Sopenharmony_ci}
82e41f4b71Sopenharmony_ci@Component
83e41f4b71Sopenharmony_cistruct SpecialImage {
84e41f4b71Sopenharmony_ci  @ObjectLink uiStyle: UIStyle;
85e41f4b71Sopenharmony_ci  private isRenderSpecialImage() : number { // Function to show whether the component is rendered.
86e41f4b71Sopenharmony_ci    console.log("SpecialImage is rendered");
87e41f4b71Sopenharmony_ci    return 1;
88e41f4b71Sopenharmony_ci  }
89e41f4b71Sopenharmony_ci  build() {
90e41f4b71Sopenharmony_ci    Image($r('app.media.icon'))
91e41f4b71Sopenharmony_ci      .width(this.uiStyle.imageWidth)
92e41f4b71Sopenharmony_ci      .height(this.uiStyle.imageHeight)
93e41f4b71Sopenharmony_ci      .margin({ top: 20 })
94e41f4b71Sopenharmony_ci      .translate({
95e41f4b71Sopenharmony_ci        x: this.uiStyle.translateImageX,
96e41f4b71Sopenharmony_ci        y: this.uiStyle.translateImageY
97e41f4b71Sopenharmony_ci      })
98e41f4b71Sopenharmony_ci      .opacity(this.isRenderSpecialImage()) // If the <Image> is rendered, it will call the function.
99e41f4b71Sopenharmony_ci  }
100e41f4b71Sopenharmony_ci}
101e41f4b71Sopenharmony_ci@Component
102e41f4b71Sopenharmony_cistruct CompA {
103e41f4b71Sopenharmony_ci  @ObjectLink uiStyle: UIStyle
104e41f4b71Sopenharmony_ci  // The following functions are used to show whether the component is called to be rendered.
105e41f4b71Sopenharmony_ci  private isRenderColumn() : number {
106e41f4b71Sopenharmony_ci    console.log("Column is rendered");
107e41f4b71Sopenharmony_ci    return 1;
108e41f4b71Sopenharmony_ci  }
109e41f4b71Sopenharmony_ci  private isRenderStack() : number {
110e41f4b71Sopenharmony_ci    console.log("Stack is rendered");
111e41f4b71Sopenharmony_ci    return 1;
112e41f4b71Sopenharmony_ci  }
113e41f4b71Sopenharmony_ci  private isRenderImage() : number {
114e41f4b71Sopenharmony_ci    console.log("Image is rendered");
115e41f4b71Sopenharmony_ci    return 1;
116e41f4b71Sopenharmony_ci  }
117e41f4b71Sopenharmony_ci  private isRenderText() : number {
118e41f4b71Sopenharmony_ci    console.log("Text is rendered");
119e41f4b71Sopenharmony_ci    return 1;
120e41f4b71Sopenharmony_ci  }
121e41f4b71Sopenharmony_ci  build() {
122e41f4b71Sopenharmony_ci    Column() {
123e41f4b71Sopenharmony_ci      // When you compile this code in API version 9, the IDE may tell you that
124e41f4b71Sopenharmony_ci      // "Assigning the '@ObjectLink' decorated attribute 'uiStyle' to the '@ObjectLink' decorated attribute 'uiStyle' is not allowed. <etsLint>"
125e41f4b71Sopenharmony_ci      // Yet, you can still run the code by Previewer.
126e41f4b71Sopenharmony_ci      SpecialImage({
127e41f4b71Sopenharmony_ci        uiStyle: this.uiStyle
128e41f4b71Sopenharmony_ci      })
129e41f4b71Sopenharmony_ci      Stack() {
130e41f4b71Sopenharmony_ci        Column() {
131e41f4b71Sopenharmony_ci            Image($r('app.media.icon'))
132e41f4b71Sopenharmony_ci              .opacity(this.uiStyle.alpha)
133e41f4b71Sopenharmony_ci              .scale({
134e41f4b71Sopenharmony_ci                x: this.uiStyle.scaleX,
135e41f4b71Sopenharmony_ci                y: this.uiStyle.scaleY
136e41f4b71Sopenharmony_ci              })
137e41f4b71Sopenharmony_ci              .padding(this.isRenderImage())
138e41f4b71Sopenharmony_ci              .width(300)
139e41f4b71Sopenharmony_ci              .height(300)
140e41f4b71Sopenharmony_ci        }
141e41f4b71Sopenharmony_ci        .width('100%')
142e41f4b71Sopenharmony_ci        .position({ y: -80 })
143e41f4b71Sopenharmony_ci        Stack() {
144e41f4b71Sopenharmony_ci          Text("Hello World")
145e41f4b71Sopenharmony_ci            .fontColor("#182431")
146e41f4b71Sopenharmony_ci            .fontWeight(FontWeight.Medium)
147e41f4b71Sopenharmony_ci            .fontSize(this.uiStyle.fontSize)
148e41f4b71Sopenharmony_ci            .opacity(this.isRenderText())
149e41f4b71Sopenharmony_ci            .margin({ top: 12 })
150e41f4b71Sopenharmony_ci        }
151e41f4b71Sopenharmony_ci        .opacity(this.isRenderStack())
152e41f4b71Sopenharmony_ci        .position({
153e41f4b71Sopenharmony_ci          x: this.uiStyle.posX,
154e41f4b71Sopenharmony_ci          y: this.uiStyle.posY
155e41f4b71Sopenharmony_ci        })
156e41f4b71Sopenharmony_ci        .width('100%')
157e41f4b71Sopenharmony_ci        .height('100%')
158e41f4b71Sopenharmony_ci      }
159e41f4b71Sopenharmony_ci      .margin({ top: 50 })
160e41f4b71Sopenharmony_ci      .borderRadius(this.uiStyle.borderRadius)
161e41f4b71Sopenharmony_ci      .opacity(this.isRenderStack())
162e41f4b71Sopenharmony_ci      .backgroundColor("#FFFFFF")
163e41f4b71Sopenharmony_ci      .width(this.uiStyle.width)
164e41f4b71Sopenharmony_ci      .height(this.uiStyle.height)
165e41f4b71Sopenharmony_ci      .translate({
166e41f4b71Sopenharmony_ci        x: this.uiStyle.translateX,
167e41f4b71Sopenharmony_ci        y: this.uiStyle.translateY
168e41f4b71Sopenharmony_ci      })
169e41f4b71Sopenharmony_ci      Column() {
170e41f4b71Sopenharmony_ci        Button("Move")
171e41f4b71Sopenharmony_ci          .width(312)
172e41f4b71Sopenharmony_ci          .fontSize(20)
173e41f4b71Sopenharmony_ci          .backgroundColor("#FF007DFF")
174e41f4b71Sopenharmony_ci          .margin({ bottom: 10 })
175e41f4b71Sopenharmony_ci          .onClick(() => {
176e41f4b71Sopenharmony_ci            animateTo({
177e41f4b71Sopenharmony_ci              duration: 500
178e41f4b71Sopenharmony_ci            },() => {
179e41f4b71Sopenharmony_ci              this.uiStyle.translateY = (this.uiStyle.translateY + 180) % 250;
180e41f4b71Sopenharmony_ci            })
181e41f4b71Sopenharmony_ci          })
182e41f4b71Sopenharmony_ci        Button("Scale")
183e41f4b71Sopenharmony_ci          .borderRadius(20)
184e41f4b71Sopenharmony_ci          .backgroundColor("#FF007DFF")
185e41f4b71Sopenharmony_ci          .fontSize(20)
186e41f4b71Sopenharmony_ci          .width(312)
187e41f4b71Sopenharmony_ci          .onClick(() => {
188e41f4b71Sopenharmony_ci            this.uiStyle.scaleX = (this.uiStyle.scaleX + 0.6) % 0.8;
189e41f4b71Sopenharmony_ci          })
190e41f4b71Sopenharmony_ci      }
191e41f4b71Sopenharmony_ci      .position({
192e41f4b71Sopenharmony_ci        y:666
193e41f4b71Sopenharmony_ci      })
194e41f4b71Sopenharmony_ci      .height('100%')
195e41f4b71Sopenharmony_ci      .width('100%')
196e41f4b71Sopenharmony_ci
197e41f4b71Sopenharmony_ci    }
198e41f4b71Sopenharmony_ci    .opacity(this.isRenderColumn())
199e41f4b71Sopenharmony_ci    .width('100%')
200e41f4b71Sopenharmony_ci    .height('100%')
201e41f4b71Sopenharmony_ci
202e41f4b71Sopenharmony_ci  }
203e41f4b71Sopenharmony_ci}
204e41f4b71Sopenharmony_ci@Entry
205e41f4b71Sopenharmony_ci@Component
206e41f4b71Sopenharmony_cistruct Page {
207e41f4b71Sopenharmony_ci  @State uiStyle: UIStyle = new UIStyle();
208e41f4b71Sopenharmony_ci  build() {
209e41f4b71Sopenharmony_ci    Stack() {
210e41f4b71Sopenharmony_ci      CompA({
211e41f4b71Sopenharmony_ci        uiStyle: this.uiStyle
212e41f4b71Sopenharmony_ci      })
213e41f4b71Sopenharmony_ci    }
214e41f4b71Sopenharmony_ci    .backgroundColor("#F1F3F5")
215e41f4b71Sopenharmony_ci  }
216e41f4b71Sopenharmony_ci}
217e41f4b71Sopenharmony_ci```
218e41f4b71Sopenharmony_ci
219e41f4b71Sopenharmony_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 according to the mechanism described above, even though they actually do not need to be re-rendered (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 values of **translateX** and **translateY** change multiple times. As a result, redundant re-renders occur at each frame, which greatly worsen the application performance.
220e41f4b71Sopenharmony_ci
221e41f4b71Sopenharmony_ciThe following figure shows the code running.
222e41f4b71Sopenharmony_ci
223e41f4b71Sopenharmony_ci![precisely-control-render-scope-02.gif](figures/precisely-control-render-scope-02.gif)
224e41f4b71Sopenharmony_ci
225e41f4b71Sopenharmony_ciTo precisely control the component render scope and avoid redundant re-renders, it is recommended that you divide a large property object into several small property objects.
226e41f4b71Sopenharmony_ci
227e41f4b71Sopenharmony_ciTo achieve this purpose, it is first necessary to understand the mechanism for property change observation.
228e41f4b71Sopenharmony_ci
229e41f4b71Sopenharmony_ciBelow is sample code:
230e41f4b71Sopenharmony_ci
231e41f4b71Sopenharmony_ci```TS
232e41f4b71Sopenharmony_ci@Observed
233e41f4b71Sopenharmony_ciclass ClassB {
234e41f4b71Sopenharmony_ci  subProp1: number = 100;
235e41f4b71Sopenharmony_ci}
236e41f4b71Sopenharmony_ci@Observed
237e41f4b71Sopenharmony_ciclass ClassA {
238e41f4b71Sopenharmony_ci  prop1: number = 0;
239e41f4b71Sopenharmony_ci  prop2: string = "This is Prop2";
240e41f4b71Sopenharmony_ci  prop3: ClassB = new ClassB();
241e41f4b71Sopenharmony_ci}
242e41f4b71Sopenharmony_ci@Component
243e41f4b71Sopenharmony_cistruct CompA {
244e41f4b71Sopenharmony_ci  @ObjectLink a: ClassA;
245e41f4b71Sopenharmony_ci  private sizeFont: number = 30; // the private variable does not invoke rendering
246e41f4b71Sopenharmony_ci  private isRenderText() : number {
247e41f4b71Sopenharmony_ci    this.sizeFont++; // the change of sizeFont will not invoke rendering, but showing that the function is called
248e41f4b71Sopenharmony_ci    console.log("Text prop2 is rendered");
249e41f4b71Sopenharmony_ci    return this.sizeFont;
250e41f4b71Sopenharmony_ci  }
251e41f4b71Sopenharmony_ci  build() {
252e41f4b71Sopenharmony_ci    Column() {
253e41f4b71Sopenharmony_ci      Text(this.a.prop2) // When this.a.prop1 changes, it will invoke <Text> re-rendering.
254e41f4b71Sopenharmony_ci        .margin({ bottom: 10 })
255e41f4b71Sopenharmony_ci        .fontSize(this.isRenderText()) // If the <Text> renders, the function isRenderText will be called.
256e41f4b71Sopenharmony_ci      Text("subProp1 : " + this.a.prop3.subProp1) //the Text can not observe the change of subProp1
257e41f4b71Sopenharmony_ci        .fontSize(30)
258e41f4b71Sopenharmony_ci    }
259e41f4b71Sopenharmony_ci  }
260e41f4b71Sopenharmony_ci}
261e41f4b71Sopenharmony_ci@Entry
262e41f4b71Sopenharmony_ci@Component
263e41f4b71Sopenharmony_cistruct Page {
264e41f4b71Sopenharmony_ci  @State a: ClassA = new ClassA();
265e41f4b71Sopenharmony_ci  build() {
266e41f4b71Sopenharmony_ci    Row() {
267e41f4b71Sopenharmony_ci      Column() {
268e41f4b71Sopenharmony_ci        Text("Prop1: " + this.a.prop1)
269e41f4b71Sopenharmony_ci          .margin({ bottom: 20 })
270e41f4b71Sopenharmony_ci          .fontSize(50)
271e41f4b71Sopenharmony_ci        CompA({a: this.a})
272e41f4b71Sopenharmony_ci        Button("Change prop1")
273e41f4b71Sopenharmony_ci          .width(200)
274e41f4b71Sopenharmony_ci          .fontSize(20)
275e41f4b71Sopenharmony_ci          .backgroundColor("#FF007DFF")
276e41f4b71Sopenharmony_ci          .margin({
277e41f4b71Sopenharmony_ci            top: 10,
278e41f4b71Sopenharmony_ci            bottom: 10
279e41f4b71Sopenharmony_ci          })
280e41f4b71Sopenharmony_ci          .onClick(() => {
281e41f4b71Sopenharmony_ci            this.a.prop1 = this.a.prop1 + 1 ;
282e41f4b71Sopenharmony_ci          })
283e41f4b71Sopenharmony_ci        Button("Change subProp1")
284e41f4b71Sopenharmony_ci          .width(200)
285e41f4b71Sopenharmony_ci          .fontSize(20)
286e41f4b71Sopenharmony_ci          .backgroundColor("#FF007DFF")
287e41f4b71Sopenharmony_ci          .onClick(() => {
288e41f4b71Sopenharmony_ci            this.a.prop3.subProp1 = this.a.prop3.subProp1 + 1;
289e41f4b71Sopenharmony_ci          })
290e41f4b71Sopenharmony_ci      }
291e41f4b71Sopenharmony_ci      .width('100%')
292e41f4b71Sopenharmony_ci    }
293e41f4b71Sopenharmony_ci    .width('100%')
294e41f4b71Sopenharmony_ci    .height('100%')
295e41f4b71Sopenharmony_ci  }
296e41f4b71Sopenharmony_ci}
297e41f4b71Sopenharmony_ci```
298e41f4b71Sopenharmony_ci
299e41f4b71Sopenharmony_ciIn the preceding example, when **Change subProp1** is clicked, you can find that the page is not re-rendered. This is because the change to **subProp1** is not observed by the component. When **Change prop1** is clicked, the page is re-rendered, with the latest values of **prop1** and **subProp1** displayed. According to the ArkUI state management mechanism, the state variable can only observe the change at the first layer. For **Change subProp1**, the property value changes at the second layer and therefore cannot be observed. In other words, the change of **this.a.prop3.subProp1** does not cause component re-renders, even if the value of **subProp1** has changed. In comparison, the change of **this.a.prop1** causes component re-renders.
300e41f4b71Sopenharmony_ci
301e41f4b71Sopenharmony_ciThe following figure shows the code running.
302e41f4b71Sopenharmony_ci
303e41f4b71Sopenharmony_ci![precisely-control-render-scope-03.gif](figures/precisely-control-render-scope-03.gif)
304e41f4b71Sopenharmony_ci
305e41f4b71Sopenharmony_ciWith this mechanism of property change observation, the render scope of components can be precisely controlled.
306e41f4b71Sopenharmony_ci
307e41f4b71Sopenharmony_ci```ts
308e41f4b71Sopenharmony_ci@Observed
309e41f4b71Sopenharmony_ciclass ClassB {
310e41f4b71Sopenharmony_ci  subProp1: number = 100;
311e41f4b71Sopenharmony_ci}
312e41f4b71Sopenharmony_ci@Observed
313e41f4b71Sopenharmony_ciclass ClassA {
314e41f4b71Sopenharmony_ci  prop1: number = 0;
315e41f4b71Sopenharmony_ci  prop2: string = "This is Prop2";
316e41f4b71Sopenharmony_ci  prop3: ClassB = new ClassB();
317e41f4b71Sopenharmony_ci}
318e41f4b71Sopenharmony_ci@Component
319e41f4b71Sopenharmony_cistruct CompA {
320e41f4b71Sopenharmony_ci  @ObjectLink a: ClassA;
321e41f4b71Sopenharmony_ci  @ObjectLink b: ClassB; // A new @ObjectLink decorated variable.
322e41f4b71Sopenharmony_ci  private sizeFont: number = 30;
323e41f4b71Sopenharmony_ci  private isRenderText() : number {
324e41f4b71Sopenharmony_ci    this.sizeFont++;
325e41f4b71Sopenharmony_ci    console.log("Text prop2 is rendered");
326e41f4b71Sopenharmony_ci    return this.sizeFont;
327e41f4b71Sopenharmony_ci  }
328e41f4b71Sopenharmony_ci  private isRenderTextSubProp1() : number {
329e41f4b71Sopenharmony_ci    this.sizeFont++;
330e41f4b71Sopenharmony_ci    console.log("Text subProp1 is rendered");
331e41f4b71Sopenharmony_ci    return this.sizeFont;
332e41f4b71Sopenharmony_ci  }
333e41f4b71Sopenharmony_ci  build() {
334e41f4b71Sopenharmony_ci    Column() {
335e41f4b71Sopenharmony_ci      Text(this.a.prop2) // When this.a.prop1 changes, it will invoke <Text> re-rendering.
336e41f4b71Sopenharmony_ci        .margin({ bottom: 10 })
337e41f4b71Sopenharmony_ci        .fontSize(this.isRenderText()) // If the <Text> renders, the function isRenderText will be called.
338e41f4b71Sopenharmony_ci      Text("subProp1 : " + this.b.subProp1) // Use directly b rather than a.prop3.
339e41f4b71Sopenharmony_ci        .fontSize(30)
340e41f4b71Sopenharmony_ci        .opacity(this.isRenderTextSubProp1())
341e41f4b71Sopenharmony_ci    }
342e41f4b71Sopenharmony_ci  }
343e41f4b71Sopenharmony_ci}
344e41f4b71Sopenharmony_ci@Entry
345e41f4b71Sopenharmony_ci@Component
346e41f4b71Sopenharmony_cistruct Page {
347e41f4b71Sopenharmony_ci  @State a: ClassA = new ClassA();
348e41f4b71Sopenharmony_ci  build() {
349e41f4b71Sopenharmony_ci    Row() {
350e41f4b71Sopenharmony_ci      Column() {
351e41f4b71Sopenharmony_ci        Text("Prop1: " + this.a.prop1)
352e41f4b71Sopenharmony_ci          .margin({ bottom: 20 })
353e41f4b71Sopenharmony_ci          .fontSize(50)
354e41f4b71Sopenharmony_ci        CompA({
355e41f4b71Sopenharmony_ci          a: this.a,
356e41f4b71Sopenharmony_ci          b: this.a.prop3
357e41f4b71Sopenharmony_ci        })
358e41f4b71Sopenharmony_ci        Button("Change prop1")
359e41f4b71Sopenharmony_ci          .width(200)
360e41f4b71Sopenharmony_ci          .fontSize(20)
361e41f4b71Sopenharmony_ci          .backgroundColor("#FF007DFF")
362e41f4b71Sopenharmony_ci          .margin({
363e41f4b71Sopenharmony_ci            top: 10,
364e41f4b71Sopenharmony_ci            bottom: 10
365e41f4b71Sopenharmony_ci          })
366e41f4b71Sopenharmony_ci          .onClick(() => {
367e41f4b71Sopenharmony_ci            this.a.prop1 = this.a.prop1 + 1 ;
368e41f4b71Sopenharmony_ci          })
369e41f4b71Sopenharmony_ci        Button("Change subProp1")
370e41f4b71Sopenharmony_ci          .width(200)
371e41f4b71Sopenharmony_ci          .fontSize(20)
372e41f4b71Sopenharmony_ci          .backgroundColor("#FF007DFF")
373e41f4b71Sopenharmony_ci          .margin({
374e41f4b71Sopenharmony_ci            top: 10,
375e41f4b71Sopenharmony_ci            bottom: 10
376e41f4b71Sopenharmony_ci          })
377e41f4b71Sopenharmony_ci          .onClick(() => {
378e41f4b71Sopenharmony_ci            this.a.prop3.subProp1 = this.a.prop3.subProp1 + 1;
379e41f4b71Sopenharmony_ci          })
380e41f4b71Sopenharmony_ci      }
381e41f4b71Sopenharmony_ci      .width('100%')
382e41f4b71Sopenharmony_ci    }
383e41f4b71Sopenharmony_ci    .width('100%')
384e41f4b71Sopenharmony_ci    .height('100%')
385e41f4b71Sopenharmony_ci  }
386e41f4b71Sopenharmony_ci}
387e41f4b71Sopenharmony_ci```
388e41f4b71Sopenharmony_ci
389e41f4b71Sopenharmony_ciIn the preceding example, a new variable **b** decorated by @ObjectLink is defined in **CompA**. When **CompA** is created on the page, **prop3** in object **a** is passed to **b**. In this way, **b** can be directly used in **CompA**. This means that, in effect, **CompA** is associated with **b** and can observe the change of **subProp1** in **b**. When **Change subProp1** is clicked, the associated **\<Text>** component is re-rendered, but other components are not (because these components are associated with **a**). Similarly, changes to other properties in **a** do not cause the **\<Text>** component to be re-rendered.
390e41f4b71Sopenharmony_ci
391e41f4b71Sopenharmony_ciThe following figure shows the code running.
392e41f4b71Sopenharmony_ci
393e41f4b71Sopenharmony_ci![precisely-control-render-scope-04.gif](figures/precisely-control-render-scope-04.gif)
394e41f4b71Sopenharmony_ci
395e41f4b71Sopenharmony_ciBy using the aforementioned method, properties in the foregoing complex redundant re-render scenario can be divided to optimize performance.
396e41f4b71Sopenharmony_ci
397e41f4b71Sopenharmony_ci```ts
398e41f4b71Sopenharmony_ci@Observed
399e41f4b71Sopenharmony_ciclass NeedRenderImage { // Properties only used in the same component can be divided into the same new divided class.
400e41f4b71Sopenharmony_ci  public translateImageX: number = 0;
401e41f4b71Sopenharmony_ci  public translateImageY: number = 0;
402e41f4b71Sopenharmony_ci  public imageWidth:number = 78;
403e41f4b71Sopenharmony_ci  public imageHeight:number = 78;
404e41f4b71Sopenharmony_ci}
405e41f4b71Sopenharmony_ci@Observed
406e41f4b71Sopenharmony_ciclass NeedRenderScale { // Properties usually used together can be divided into the same new child class.
407e41f4b71Sopenharmony_ci  public scaleX: number = 0.3;
408e41f4b71Sopenharmony_ci  public scaleY: number = 0.3;
409e41f4b71Sopenharmony_ci}
410e41f4b71Sopenharmony_ci@Observed
411e41f4b71Sopenharmony_ciclass NeedRenderAlpha { // Properties that may be used in different places can be divided into the same new child class.
412e41f4b71Sopenharmony_ci  public alpha: number = 0.5;
413e41f4b71Sopenharmony_ci}
414e41f4b71Sopenharmony_ci@Observed
415e41f4b71Sopenharmony_ciclass NeedRenderSize { // Properties usually used together can be divided into the same new child class.
416e41f4b71Sopenharmony_ci  public width: number = 336;
417e41f4b71Sopenharmony_ci  public height: number = 178;
418e41f4b71Sopenharmony_ci}
419e41f4b71Sopenharmony_ci@Observed
420e41f4b71Sopenharmony_ciclass NeedRenderPos { // Properties usually used together can be divided into the same new child class.
421e41f4b71Sopenharmony_ci  public posX: number = 10;
422e41f4b71Sopenharmony_ci  public posY: number = 50;
423e41f4b71Sopenharmony_ci}
424e41f4b71Sopenharmony_ci@Observed
425e41f4b71Sopenharmony_ciclass NeedRenderBorderRadius { // Properties that may be used in different places can be divided into the same new child class.
426e41f4b71Sopenharmony_ci  public borderRadius: number = 24;
427e41f4b71Sopenharmony_ci}
428e41f4b71Sopenharmony_ci@Observed
429e41f4b71Sopenharmony_ciclass NeedRenderFontSize { // Properties that may be used in different places can be divided into the same new child class.
430e41f4b71Sopenharmony_ci  public fontSize: number = 20;
431e41f4b71Sopenharmony_ci}
432e41f4b71Sopenharmony_ci@Observed
433e41f4b71Sopenharmony_ciclass NeedRenderTranslate { // Properties usually used together can be divided into the same new child class.
434e41f4b71Sopenharmony_ci  public translateX: number = 0;
435e41f4b71Sopenharmony_ci  public translateY: number = 0;
436e41f4b71Sopenharmony_ci}
437e41f4b71Sopenharmony_ci@Observed
438e41f4b71Sopenharmony_ciclass UIStyle {
439e41f4b71Sopenharmony_ci  // Define a new variable instead of using the old one.
440e41f4b71Sopenharmony_ci  needRenderTranslate: NeedRenderTranslate = new NeedRenderTranslate();
441e41f4b71Sopenharmony_ci  needRenderFontSize: NeedRenderFontSize = new NeedRenderFontSize();
442e41f4b71Sopenharmony_ci  needRenderBorderRadius: NeedRenderBorderRadius = new NeedRenderBorderRadius();
443e41f4b71Sopenharmony_ci  needRenderPos: NeedRenderPos = new NeedRenderPos();
444e41f4b71Sopenharmony_ci  needRenderSize: NeedRenderSize = new NeedRenderSize();
445e41f4b71Sopenharmony_ci  needRenderAlpha: NeedRenderAlpha = new NeedRenderAlpha();
446e41f4b71Sopenharmony_ci  needRenderScale: NeedRenderScale = new NeedRenderScale();
447e41f4b71Sopenharmony_ci  needRenderImage: NeedRenderImage = new NeedRenderImage();
448e41f4b71Sopenharmony_ci}
449e41f4b71Sopenharmony_ci@Component
450e41f4b71Sopenharmony_cistruct SpecialImage {
451e41f4b71Sopenharmony_ci  @ObjectLink uiStyle : UIStyle;
452e41f4b71Sopenharmony_ci  @ObjectLink needRenderImage: NeedRenderImage // Receive the new class from its parent component.
453e41f4b71Sopenharmony_ci  private isRenderSpecialImage() : number { // Function to show whether the component is rendered.
454e41f4b71Sopenharmony_ci    console.log("SpecialImage is rendered");
455e41f4b71Sopenharmony_ci    return 1;
456e41f4b71Sopenharmony_ci  }
457e41f4b71Sopenharmony_ci  build() {
458e41f4b71Sopenharmony_ci    Image($r('app.media.icon'))
459e41f4b71Sopenharmony_ci      .width(this.needRenderImage.imageWidth) // Attention: Use this.needRenderImage.xxx rather than this.uiStyle.needRenderImage.xxx.
460e41f4b71Sopenharmony_ci      .height(this.needRenderImage.imageHeight)
461e41f4b71Sopenharmony_ci      .margin({top:20})
462e41f4b71Sopenharmony_ci      .translate({
463e41f4b71Sopenharmony_ci        x: this.needRenderImage.translateImageX,
464e41f4b71Sopenharmony_ci        y: this.needRenderImage.translateImageY
465e41f4b71Sopenharmony_ci      })
466e41f4b71Sopenharmony_ci      .opacity(this.isRenderSpecialImage()) // If the <Image> is rendered, it will call the function.
467e41f4b71Sopenharmony_ci  }
468e41f4b71Sopenharmony_ci}
469e41f4b71Sopenharmony_ci@Component
470e41f4b71Sopenharmony_cistruct CompA {
471e41f4b71Sopenharmony_ci  @ObjectLink uiStyle: UIStyle;
472e41f4b71Sopenharmony_ci  @ObjectLink needRenderTranslate: NeedRenderTranslate; // Receive the new class from its parent component.
473e41f4b71Sopenharmony_ci  @ObjectLink needRenderFontSize: NeedRenderFontSize;
474e41f4b71Sopenharmony_ci  @ObjectLink needRenderBorderRadius: NeedRenderBorderRadius;
475e41f4b71Sopenharmony_ci  @ObjectLink needRenderPos: NeedRenderPos;
476e41f4b71Sopenharmony_ci  @ObjectLink needRenderSize: NeedRenderSize;
477e41f4b71Sopenharmony_ci  @ObjectLink needRenderAlpha: NeedRenderAlpha;
478e41f4b71Sopenharmony_ci  @ObjectLink needRenderScale: NeedRenderScale;
479e41f4b71Sopenharmony_ci  // The following functions are used to show whether the component is called to be rendered.
480e41f4b71Sopenharmony_ci  private isRenderColumn() : number {
481e41f4b71Sopenharmony_ci    console.log("Column is rendered");
482e41f4b71Sopenharmony_ci    return 1;
483e41f4b71Sopenharmony_ci  }
484e41f4b71Sopenharmony_ci  private isRenderStack() : number {
485e41f4b71Sopenharmony_ci    console.log("Stack is rendered");
486e41f4b71Sopenharmony_ci    return 1;
487e41f4b71Sopenharmony_ci  }
488e41f4b71Sopenharmony_ci  private isRenderImage() : number {
489e41f4b71Sopenharmony_ci    console.log("Image is rendered");
490e41f4b71Sopenharmony_ci    return 1;
491e41f4b71Sopenharmony_ci  }
492e41f4b71Sopenharmony_ci  private isRenderText() : number {
493e41f4b71Sopenharmony_ci    console.log("Text is rendered");
494e41f4b71Sopenharmony_ci    return 1;
495e41f4b71Sopenharmony_ci  }
496e41f4b71Sopenharmony_ci  build() {
497e41f4b71Sopenharmony_ci    Column() {
498e41f4b71Sopenharmony_ci      // When you compile this code in API version 9, the IDE may tell you that
499e41f4b71Sopenharmony_ci      // "Assigning the '@ObjectLink' decorated attribute 'uiStyle' to the '@ObjectLink' decorated attribute 'uiStyle' is not allowed. <etsLint>"
500e41f4b71Sopenharmony_ci      // "Assigning the '@ObjectLink' decorated attribute 'uiStyle' to the '@ObjectLink' decorated attribute 'needRenderImage' is not allowed. <etsLint>"
501e41f4b71Sopenharmony_ci      // Yet, you can still run the code by Previewer.
502e41f4b71Sopenharmony_ci      SpecialImage({
503e41f4b71Sopenharmony_ci        uiStyle: this.uiStyle,
504e41f4b71Sopenharmony_ci        needRenderImage: this.uiStyle.needRenderImage // Send it to its child.
505e41f4b71Sopenharmony_ci      })
506e41f4b71Sopenharmony_ci      Stack() {
507e41f4b71Sopenharmony_ci        Column() {
508e41f4b71Sopenharmony_ci          Image($r('app.media.icon'))
509e41f4b71Sopenharmony_ci            .opacity(this.needRenderAlpha.alpha)
510e41f4b71Sopenharmony_ci            .scale({
511e41f4b71Sopenharmony_ci              x: this.needRenderScale.scaleX, // Use this.needRenderXxx.xxx rather than this.uiStyle.needRenderXxx.xxx.
512e41f4b71Sopenharmony_ci              y: this.needRenderScale.scaleY
513e41f4b71Sopenharmony_ci            })
514e41f4b71Sopenharmony_ci            .padding(this.isRenderImage())
515e41f4b71Sopenharmony_ci            .width(300)
516e41f4b71Sopenharmony_ci            .height(300)
517e41f4b71Sopenharmony_ci        }
518e41f4b71Sopenharmony_ci        .width('100%')
519e41f4b71Sopenharmony_ci        .position({ y: -80 })
520e41f4b71Sopenharmony_ci
521e41f4b71Sopenharmony_ci        Stack() {
522e41f4b71Sopenharmony_ci          Text("Hello World")
523e41f4b71Sopenharmony_ci            .fontColor("#182431")
524e41f4b71Sopenharmony_ci            .fontWeight(FontWeight.Medium)
525e41f4b71Sopenharmony_ci            .fontSize(this.needRenderFontSize.fontSize)
526e41f4b71Sopenharmony_ci            .opacity(this.isRenderText())
527e41f4b71Sopenharmony_ci            .margin({ top: 12 })
528e41f4b71Sopenharmony_ci        }
529e41f4b71Sopenharmony_ci        .opacity(this.isRenderStack())
530e41f4b71Sopenharmony_ci        .position({
531e41f4b71Sopenharmony_ci          x: this.needRenderPos.posX,
532e41f4b71Sopenharmony_ci          y: this.needRenderPos.posY
533e41f4b71Sopenharmony_ci        })
534e41f4b71Sopenharmony_ci        .width('100%')
535e41f4b71Sopenharmony_ci        .height('100%')
536e41f4b71Sopenharmony_ci      }
537e41f4b71Sopenharmony_ci      .margin({ top: 50 })
538e41f4b71Sopenharmony_ci      .borderRadius(this.needRenderBorderRadius.borderRadius)
539e41f4b71Sopenharmony_ci      .opacity(this.isRenderStack())
540e41f4b71Sopenharmony_ci      .backgroundColor("#FFFFFF")
541e41f4b71Sopenharmony_ci      .width(this.needRenderSize.width)
542e41f4b71Sopenharmony_ci      .height(this.needRenderSize.height)
543e41f4b71Sopenharmony_ci      .translate({
544e41f4b71Sopenharmony_ci        x: this.needRenderTranslate.translateX,
545e41f4b71Sopenharmony_ci        y: this.needRenderTranslate.translateY
546e41f4b71Sopenharmony_ci      })
547e41f4b71Sopenharmony_ci
548e41f4b71Sopenharmony_ci      Column() {
549e41f4b71Sopenharmony_ci        Button("Move")
550e41f4b71Sopenharmony_ci          .width(312)
551e41f4b71Sopenharmony_ci          .fontSize(20)
552e41f4b71Sopenharmony_ci          .backgroundColor("#FF007DFF")
553e41f4b71Sopenharmony_ci          .margin({ bottom: 10 })
554e41f4b71Sopenharmony_ci          .onClick(() => {
555e41f4b71Sopenharmony_ci            animateTo({
556e41f4b71Sopenharmony_ci              duration: 500
557e41f4b71Sopenharmony_ci            }, () => {
558e41f4b71Sopenharmony_ci              this.needRenderTranslate.translateY = (this.needRenderTranslate.translateY + 180) % 250;
559e41f4b71Sopenharmony_ci            })
560e41f4b71Sopenharmony_ci          })
561e41f4b71Sopenharmony_ci        Button("Scale")
562e41f4b71Sopenharmony_ci          .borderRadius(20)
563e41f4b71Sopenharmony_ci          .backgroundColor("#FF007DFF")
564e41f4b71Sopenharmony_ci          .fontSize(20)
565e41f4b71Sopenharmony_ci          .width(312)
566e41f4b71Sopenharmony_ci          .margin({ bottom: 10 })
567e41f4b71Sopenharmony_ci          .onClick(() => {
568e41f4b71Sopenharmony_ci            this.needRenderScale.scaleX = (this.needRenderScale.scaleX + 0.6) % 0.8;
569e41f4b71Sopenharmony_ci          })
570e41f4b71Sopenharmony_ci        Button("Change Image")
571e41f4b71Sopenharmony_ci          .borderRadius(20)
572e41f4b71Sopenharmony_ci          .backgroundColor("#FF007DFF")
573e41f4b71Sopenharmony_ci          .fontSize(20)
574e41f4b71Sopenharmony_ci          .width(312)
575e41f4b71Sopenharmony_ci          .onClick(() => { // In the parent component, still use this.uiStyle.needRenderXxx.xxx to change the properties.
576e41f4b71Sopenharmony_ci            this.uiStyle.needRenderImage.imageWidth = (this.uiStyle.needRenderImage.imageWidth + 30) % 160;
577e41f4b71Sopenharmony_ci            this.uiStyle.needRenderImage.imageHeight = (this.uiStyle.needRenderImage.imageHeight + 30) % 160;
578e41f4b71Sopenharmony_ci          })
579e41f4b71Sopenharmony_ci      }
580e41f4b71Sopenharmony_ci      .position({
581e41f4b71Sopenharmony_ci        y: 616
582e41f4b71Sopenharmony_ci      })
583e41f4b71Sopenharmony_ci      .height('100%')
584e41f4b71Sopenharmony_ci      .width('100%')
585e41f4b71Sopenharmony_ci    }
586e41f4b71Sopenharmony_ci    .opacity(this.isRenderColumn())
587e41f4b71Sopenharmony_ci    .width('100%')
588e41f4b71Sopenharmony_ci    .height('100%')
589e41f4b71Sopenharmony_ci  }
590e41f4b71Sopenharmony_ci}
591e41f4b71Sopenharmony_ci@Entry
592e41f4b71Sopenharmony_ci@Component
593e41f4b71Sopenharmony_cistruct Page {
594e41f4b71Sopenharmony_ci  @State uiStyle: UIStyle = new UIStyle();
595e41f4b71Sopenharmony_ci  build() {
596e41f4b71Sopenharmony_ci    Stack() {
597e41f4b71Sopenharmony_ci      CompA({
598e41f4b71Sopenharmony_ci        uiStyle: this.uiStyle,
599e41f4b71Sopenharmony_ci        needRenderTranslate: this.uiStyle.needRenderTranslate, // Send all the new class child need.
600e41f4b71Sopenharmony_ci        needRenderFontSize: this.uiStyle.needRenderFontSize,
601e41f4b71Sopenharmony_ci        needRenderBorderRadius: this.uiStyle.needRenderBorderRadius,
602e41f4b71Sopenharmony_ci        needRenderPos: this.uiStyle.needRenderPos,
603e41f4b71Sopenharmony_ci        needRenderSize: this.uiStyle.needRenderSize,
604e41f4b71Sopenharmony_ci        needRenderAlpha: this.uiStyle.needRenderAlpha,
605e41f4b71Sopenharmony_ci        needRenderScale: this.uiStyle.needRenderScale
606e41f4b71Sopenharmony_ci      })
607e41f4b71Sopenharmony_ci    }
608e41f4b71Sopenharmony_ci    .backgroundColor("#F1F3F5")
609e41f4b71Sopenharmony_ci  }
610e41f4b71Sopenharmony_ci}
611e41f4b71Sopenharmony_ci```
612e41f4b71Sopenharmony_ci
613e41f4b71Sopenharmony_ciThe following figure shows the code running.
614e41f4b71Sopenharmony_ci
615e41f4b71Sopenharmony_ci![precisely-control-render-scope-05.gif](figures/precisely-control-render-scope-05.gif)
616e41f4b71Sopenharmony_ci
617e41f4b71Sopenharmony_ciIn the preceding example, the 15 properties in the original class are divided into eight child classes, and the corresponding adaptation is performed on the binding between properties and components. Division of properties complies with the following principles:
618e41f4b71Sopenharmony_ci
619e41f4b71Sopenharmony_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. In this scenario, divide the properties or review the view model design.
620e41f4b71Sopenharmony_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).
621e41f4b71Sopenharmony_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 is not related to other properties, for example, **.opacity** and **.borderRadius** (which usually work on their own).
622e41f4b71Sopenharmony_ci
623e41f4b71Sopenharmony_ciAfter properties are divided, use the following format to bind components using the properties:
624e41f4b71Sopenharmony_ci
625e41f4b71Sopenharmony_ci```ts
626e41f4b71Sopenharmony_ci.property(this.needRenderXxx.xxx)
627e41f4b71Sopenharmony_ci
628e41f4b71Sopenharmony_ci// sample
629e41f4b71Sopenharmony_ciText("some text")
630e41f4b71Sopenharmony_ci.width(this.needRenderSize.width)
631e41f4b71Sopenharmony_ci.height(this.needRenderSize.height)
632e41f4b71Sopenharmony_ci.opacity(this.needRenderAlpha.alpha)
633e41f4b71Sopenharmony_ci```
634e41f4b71Sopenharmony_ci
635e41f4b71Sopenharmony_ciIf changes of a property apply to the parent component, the property can be changed through the outer parent class.
636e41f4b71Sopenharmony_ci
637e41f4b71Sopenharmony_ci```ts
638e41f4b71Sopenharmony_ci// In parent Component
639e41f4b71Sopenharmony_cithis.parent.needRenderXxx.xxx = x;
640e41f4b71Sopenharmony_ci
641e41f4b71Sopenharmony_ci// Example
642e41f4b71Sopenharmony_cithis.uiStyle.needRenderImage.imageWidth = (this.uiStyle.needRenderImage.imageWidth + 20) % 60;
643e41f4b71Sopenharmony_ci```
644e41f4b71Sopenharmony_ci
645e41f4b71Sopenharmony_ciIf changes of a property apply to the child component, it is recommended that the property be changed through the new child class.
646e41f4b71Sopenharmony_ci
647e41f4b71Sopenharmony_ci```ts
648e41f4b71Sopenharmony_ci// In child Component
649e41f4b71Sopenharmony_cithis.needRenderXxx.xxx = x;
650e41f4b71Sopenharmony_ci
651e41f4b71Sopenharmony_ci// Example
652e41f4b71Sopenharmony_cithis.needRenderScale.scaleX = (this.needRenderScale.scaleX + 0.6) % 1
653e41f4b71Sopenharmony_ci```
654e41f4b71Sopenharmony_ci
655e41f4b71Sopenharmony_ciWhen dividing properties to speed up applications, focus on properties that change frequently.
656e41f4b71Sopenharmony_ci
657e41f4b71Sopenharmony_ciIf you want to use the divided properties in the parent component, you are advised to define a new @State decorated state variable and use them together.
658e41f4b71Sopenharmony_ci
659e41f4b71Sopenharmony_ci```ts
660e41f4b71Sopenharmony_ci@Observed
661e41f4b71Sopenharmony_ciclass NeedRenderProperty {
662e41f4b71Sopenharmony_ci  public property: number = 1;
663e41f4b71Sopenharmony_ci};
664e41f4b71Sopenharmony_ci@Observed
665e41f4b71Sopenharmony_ciclass SomeClass {
666e41f4b71Sopenharmony_ci  needRenderProperty: NeedRenderProperty = new NeedRenderProperty();
667e41f4b71Sopenharmony_ci}
668e41f4b71Sopenharmony_ci@Entry
669e41f4b71Sopenharmony_ci@Component
670e41f4b71Sopenharmony_cistruct Page {
671e41f4b71Sopenharmony_ci  @State someClass: SomeClass = new SomeClass();
672e41f4b71Sopenharmony_ci  @State needRenderProperty: NeedRenderProperty = this.someClass.needRenderProperty
673e41f4b71Sopenharmony_ci  build() {
674e41f4b71Sopenharmony_ci    Row() {
675e41f4b71Sopenharmony_ci      Column() {
676e41f4b71Sopenharmony_ci        Text("property value: " + this.needRenderProperty.property)
677e41f4b71Sopenharmony_ci          .fontSize(30)
678e41f4b71Sopenharmony_ci          .margin({ bottom: 20 })
679e41f4b71Sopenharmony_ci        Button("Change property")
680e41f4b71Sopenharmony_ci          .onClick(() => {
681e41f4b71Sopenharmony_ci            this.needRenderProperty.property++;
682e41f4b71Sopenharmony_ci          })
683e41f4b71Sopenharmony_ci      }
684e41f4b71Sopenharmony_ci      .width('100%')
685e41f4b71Sopenharmony_ci    }
686e41f4b71Sopenharmony_ci    .width('100%')
687e41f4b71Sopenharmony_ci    .height('100%')
688e41f4b71Sopenharmony_ci  }
689e41f4b71Sopenharmony_ci}
690e41f4b71Sopenharmony_ci```
691