1# \@Monitor Decorator: Listening for Value Changes of the State Variables
2
3You can use \@Monitor, a method decorator in state management V2, to enhance the capability of the state management framework to listen for the state variable changes
4
5>**NOTE**
6>
7>The \@Monitor decorator is supported since API version 12.
8>
9
10## Overview
11
12To listen for value changes of the state variables in a lower level, you can use the \@Monitor decorator:
13
14- The \@Monitor decorator can be used in custom components decorated by \@ComponentV2. But it cannot listen for the changes of the state variables that are not decorated by these decorators: [\@Local](arkts-new-local.md), [\@Param](arkts-new-param.md), [\@Provider](arkts-new-Provider-and-Consumer.md), [\@Consumer](arkts-new-Provider-and-Consumer.md), and [\@Computed](arkts-new-Computed.md).
15
16- The \@Monitor decorator can be used in a class together with [\@ObservedV2 and \@Trace](arkts-new-observedV2-and-trace.md) decorators. But it cannot be used in a class that is not decorated by \@ObservedV2. \@Monitor cannot listen for the properties that are not decorated by \@Trace.
17- When the listened property changes, the callback defined by \@Monitor will be called. Strict equality (===) is used to determine whether a property changes. If **false** is returned, the \@Monitor callback is triggered. When a property is changed for multiple times in an event, the initial value will be compared with the final value to determine whether the property is changed.
18- A single \@Monitor decorator can listen for the changes of multiple properties at the same time. When these properties change together in an event, the \@Monitor callback method is triggered only once.
19- The \@Monitor decorator has lower-level listening capability and can listen for changes of specified items in nested classes, multi-dimensional arrays, and object arrays. The observation requires that \@ObservedV2 decorate the nested class and \@Trace decorate the member properties in an object array.
20- In the inheritance scenario, you can define \@Monitor for the same property in the parent and child components for listening. When the property changes, the \@Monitor callback defined in the parent and child components is called.
21- Similar to the [\@Watch](arkts-watch.md) decorator, you should define the callback functions by yourselves. The difference is that the \@Watch decorator uses the function name as a parameter, while the \@Monitor directly decorates the callback function. For details about the comparison between \@Monitor and \@Watch, see [Comparing \@Monitor with \@Watch](#comparing-monitor-with-watch).
22
23## Limitations of the \@Watch decorator in State Management V1
24
25This V1 version cannot listen for the changes of an object, a single property in an array, or array items. It also cannot obtain the value before change.
26
27```ts
28@Observed
29class Info {
30  name: string = "Tom";
31  age: number = 25;
32}
33@Entry
34@Component
35struct Index {
36  @State @Watch('onInfoChange') info: Info = new Info();
37  @State @Watch('onNumArrChange') numArr: number[] = [1,2,3,4,5];
38
39  onInfoChange() {
40    console.log(`info after change name: ${this.info.name}, age: ${this.info.age} `);
41  }
42  onNumArrChange() {
43    console.log(`numArr after change ${JSON.stringify(this.numArr)}`);
44  }
45  build() {
46    Row() {
47      Column() {
48        Button("change info name")
49          .onClick(() => {
50            this.info.name = "Jack";
51          })
52        Button("change info age")
53          .onClick(() => {
54            this.info.age = 30;
55          })
56        Button("change numArr[2]")
57          .onClick(() => {
58            this.numArr[2] = 5;
59          })
60        Button("change numArr[3]")
61          .onClick(() => {
62            this.numArr[3] = 6;
63          })
64      }
65      .width('100%')
66    }
67    .height('100%')
68  }
69}
70```
71
72In the preceding code, when you click **change info name** to change the **name** property in **info**, or click **change info age** to change **age**, the **info** registered \@Watch callback is triggered. When you click **change numArr[2]** to change the third element in **numArr**, or click **change numArr[3]** to change the fourth element, the **numArr** registered \@Watch callback is triggered. In these two callbacks, the value before data change cannot be obtained. This makes it inconvenient for you to listen for the variable changes because you cannot find out which property or element is changed to trigger \@Watch event in a more complex scenario. Therefore, the \@Monitor decorator comes into the picture to listen for the changes of an object, a single property in an array, or an array item and obtain the value before change.
73
74## Decorator Description
75
76| \@Monitor Property Decorator| Description                                                        |
77| ------------------- | ------------------------------------------------------------ |
78| Decorator parameter         | Object property name of the string type. This decorator can listen for multiple object properties at the same time. Each property is separated by commas (,), for example, @Monitor ("prop1", "prop2"). In addition, properties such as an element in a multi-dimensional array, a property in a nested object, and a property in an object array can be listened in a lower level. For details, see [Listened Changes](#listened-changes).|
79| Decorated object           | \@Monitor decorated member method. This callback is triggered when the listened property changes. The variable of [IMonitor type](#imonitor-type) will be set as a parameter to provide related information before and after change.|
80
81## API Description
82
83### IMonitor Type
84
85Variables of the IMonitor type are used as parameters for \@Monitor to decorate a method.
86
87| Property      | Type           | Parameter         | Return Value            | Description                                                        |
88| ---------- | --------------- | ------------- | ------------------ | ------------------------------------------------------------ |
89| dirty      | Array\<string\> | None.           | None.                | Saves the changed property name.                                      |
90| value\<T\> | function        | path?: string | IMonitorValue\<T\> | Obtains the change information of a specified property (**path**). If **path** is not specified, @Monitor will return the first changed property information in the listening sequence.|
91
92### IMonitorValue\<T\> Type
93
94Saves the information about property changes, including the property name, original value, and new value.
95
96| Property  | Type  | Description                      |
97| ------ | ------ | -------------------------- |
98| before | T      | Listens for the value before the property change.    |
99| now    | T      | Listens for the current value after the property changes.|
100| path   | string | Listened property name.            |
101
102## Listened Changes
103
104### Using \@Monitor in Custom Components Decorated by \@ComponentV2
105
106When the state variables listened by \@Monitor change, the callback is triggered.
107
108- Variables listened by \@Monitor need to be decorated by \@Local, \@Param, \@Provider, \@Consumer, and \@Computed. Otherwise, they cannot be listened when they change. \@Monitor can listen for multiple state variables at the same time. Names of these variables are separated by commas (,).
109
110  ```ts
111  @Entry
112  @ComponentV2
113  struct Index {
114    @Local message: string = "Hello World";
115    @Local name: string = "Tom";
116    @Local age: number = 24;
117    @Monitor("message", "name")
118    onStrChange(monitor: IMonitor) {
119      monitor.dirty.forEach((path: string) => {
120        console.log(`${path} changed from ${monitor.value(path)?.before} to ${monitor.value(path)?.now}`)
121      })
122    }
123    build() {
124      Column() {
125        Button("change string")
126          .onClick(() => {
127            this.message += "!";
128            this.name = "Jack";
129        })
130      }
131    }
132  }
133  ```
134
135- When the state variable listened by \@Monitor is a class object, only the overall object changes can be listened. To listen for the changes of a class property, this property should be decorated by \@Trace.
136
137  ```ts
138  class Info {
139    name: string;
140    age: number;
141    constructor(name: string, age: number) {
142      this.name = name;
143      this.age = age;
144    }
145  }
146  @Entry
147  @ComponentV2
148  struct Index {
149    @Local info: Info = new Info("Tom", 25);
150    @Monitor("info")
151    infoChange(monitor: IMonitor) {
152      console.log(`info change`);
153    }
154    @Monitor("info.name")
155    infoPropertyChange(monitor: IMonitor) {
156      console.log(`info name change`);
157    }
158    build() {
159      Column() {
160        Text(`name: ${this.info.name}, age: ${this.info.age}`)
161        Button("change info")
162          .onClick(() => {
163            this.info = new Info ("Lucy", 18); // Can listen for the change.
164          })
165        Button("change info.name")
166          .onClick(() => {
167            this.info.name = "Jack"; // Cannot listen for the change.
168          })
169      }
170    }
171  }
172  ```
173
174### Using \@Monitor in Classes Decorated by \@ObservedV2
175
176When the properties listened by \@Monitor change, the callback is triggered.
177
178- The object property listened by \@Monitor should be decorated by \@Trace. Otherwise, the property cannot be listened. \@Monitor can listen for multiple properties at the same time. These properties are separated by commas (,).
179
180```ts
181@ObservedV2
182class Info {
183  @Trace name: string = "Tom";
184  @Trace region: string = "North";
185  @Trace job: string = "Teacher";
186  age: number = 25;
187  // name is decorated by @Trace. Can listen for the change.
188  @Monitor("name")
189  onNameChange(monitor: IMonitor) {
190    console.log(`name change from ${monitor.value()?.before} to ${monitor.value()?.now}`);
191  }
192  // age is not decorated by @Trace. Cannot listen for the change.
193  @Monitor("age")
194  onAgeChange(monitor: IMonitor) {
195    console.log(`age change from ${monitor.value()?.before} to ${monitor.value()?.now}`);
196  }
197  // region and job are decorated by @Trace. Can listen for the change.
198  @Monitor("region", "job")
199  onChange(monitor: IMonitor) {
200    monitor.dirty.forEach((path: string) => {
201      console.log(`${path} change from ${monitor.value(path)?.before} to ${monitor.value(path)?.now}`);
202    })
203  }
204}
205@Entry
206@ComponentV2
207struct Index {
208  info: Info = new Info();
209  build() {
210    Column() {
211      Button("change name")
212        .onClick(() => {
213          this.info.name = "Jack"; // Can trigger the onNameChange method.
214        })
215      Button("change age")
216        .onClick(() => {
217          this.info.age = 26; // Cannot trigger the onAgeChange method.
218        })
219      Button("change region")
220        .onClick(() => {
221          this.info.region = "South"; // Can trigger the onChange method.
222        })
223      Button("change job")
224        .onClick(() => {
225          this.info.job = "Driver"; // Can trigger the onChange method.
226        })
227    }
228  }
229}
230```
231
232- \@Monitor can listen for the changes of lower-level properties which should be decorated by @Trace.
233
234```ts
235@ObservedV2
236class Inner {
237  @Trace num: number = 0;
238}
239@ObservedV2
240class Outer {
241  inner: Inner = new Inner();
242  @Monitor("inner.num")
243  onChange(monitor: IMonitor) {
244    console.log(`inner.num change from ${monitor.value()?.before} to ${monitor.value()?.now}`);
245  }
246}
247@Entry
248@ComponentV2
249struct Index {
250  outer: Outer = new Outer();
251  build() {
252    Column() {
253      Button("change name")
254        .onClick(() => {
255          this.outer.inner.num = 100; // Can trigger the onChange method.
256        })
257    }
258  }
259}
260```
261
262- In the inheritance class scenario, you can listen for the same property for multiple times in the inheritance chain.
263
264```ts
265@ObservedV2
266class Base {
267  @Trace name: string;
268  // Listen for the name property of the base class.
269  @Monitor("name")
270  onBaseNameChange(monitor: IMonitor) {
271    console.log(`Base Class name change`);
272  }
273  constructor(name: string) {
274    this.name = name;
275  }
276}
277@ObservedV2
278class Derived extends Base {
279  // Listen for the name property of the inheritance class.
280  @Monitor("name")
281  onDerivedNameChange(monitor: IMonitor) {
282    console.log(`Derived Class name change`);
283  }
284  constructor(name: string) {
285    super(name);
286  }
287}
288@Entry
289@ComponentV2
290struct Index {
291  derived: Derived = new Derived("AAA");
292  build() {
293    Column() {
294      Button("change name")
295        .onClick(() => {
296          this.derived.name = "BBB"; // Can trigger the onBaseNameChange and onDerivedNameChange methods in sequence.
297        })
298    }
299  }
300}
301```
302
303### General Listening Capability
304
305\@Monitor also has some general listening capabilities.
306
307- \@Monitor can listen for items in arrays, including multi-dimensional arrays and object arrays. \@Monitor cannot listen for changes caused by calling APIs of built-in types (Array, Map, Date, and Set). When \@Monitor listens for the entire array, only the value changes to the entire array can be observed. But you can listen for the length change of the array to determine whether the array is inserted or deleted. Currently, only periods (.) can be used to listen for lower-level properties and array items.
308
309```ts
310@ObservedV2
311class Info {
312  @Trace name: string;
313  @Trace age: number;
314
315  constructor(name: string, age: number) {
316    this.name = name;
317    this.age = age;
318  }
319}
320@ObservedV2
321class ArrMonitor {
322  @Trace dimensionTwo: number[][] = [[1,1,1],[2,2,2],[3,3,3]];
323  @Trace dimensionThree: number[][][] = [[[1],[2],[3]],[[4],[5],[6]],[[7],[8],[9]]];
324  @Trace infoArr: Info[] = [new Info("Jack", 24), new Info("Lucy", 18)];
325  // dimensionTwo is a two-dimensional simple array and is decorated by @Trace. Can observe the element changes.
326  @Monitor("dimensionTwo.0.0", "dimensionTwo.1.1")
327  onDimensionTwoChange(monitor: IMonitor) {
328    monitor.dirty.forEach((path: string) => {
329      console.log(`dimensionTwo path: ${path} change from ${monitor.value(path)?.before} to ${monitor.value(path)?.now}`);
330    })
331  }
332  // dimensionThree is a three-dimensional simple array and is decorated by @Trace. Can observe the element changes.
333  @Monitor("dimensionThree.0.0.0", "dimensionThree.1.1.0")
334  onDimensionThreeChange(monitor: IMonitor) {
335    monitor.dirty.forEach((path: string) => {
336      console.log(`dimensionThree path: ${path} change from ${monitor.value(path)?.before} to ${monitor.value(path)?.now}`);
337    })
338  }
339  // name and age properties of the info class are decorated by @Trace. Can listen for the change.
340  @Monitor("infoArr.0.name", "infoArr.1.age")
341  onInfoArrPropertyChange(monitor: IMonitor) {
342    monitor.dirty.forEach((path: string) => {
343      console.log(`infoArr path:${path} change from ${monitor.value(path)?.before} to ${monitor.value(path)?.now}`);
344    })
345  }
346  // infoArr is decorated by @Trace. Can listen for the value changes.
347  @Monitor("infoArr")
348  onInfoArrChange(monitor: IMonitor) {
349    console.log(`infoArr whole change`);
350  }
351  // Can listen for the length change of the infoArr.
352  @Monitor("infoArr.length")
353  onInfoArrLengthChange(monitor: IMonitor) {
354    console.log(`infoArr length change`);
355  }
356}
357@Entry
358@ComponentV2
359struct Index {
360  arrMonitor: ArrMonitor = new ArrMonitor();
361  build() {
362    Column() {
363      Button("Change dimensionTwo")
364        .onClick(() => {
365          // Can trigger the onDimensionTwoChange method.
366          this.arrMonitor.dimensionTwo[0][0]++;
367          this.arrMonitor.dimensionTwo[1][1]++;
368        })
369      Button("Change dimensionThree")
370        .onClick(() => {
371          // Can trigger the onDimensionThreeChange method.
372          this.arrMonitor.dimensionThree[0][0][0]++;
373          this.arrMonitor.dimensionThree[1][1][0]++;
374        })
375      Button("Change info property")
376        .onClick(() => {
377          // Can trigger the onInfoArrPropertyChange method.
378          this.arrMonitor.infoArr[0].name = "Tom";
379          this.arrMonitor.infoArr[1].age = 19;
380        })
381      Button("Change whole infoArr")
382        .onClick(() => {
383          // Can trigger the onInfoArrChange, onInfoArrPropertyChange, and onInfoArrLengthChange methods.
384          this.arrMonitor.infoArr = [new Info("Cindy", 8)];
385        })
386      Button("Push new info to infoArr")
387        .onClick(() => {
388          // Can trigger the onInfoArrPropertyChange and onInfoArrLengthChange methods.
389          this.arrMonitor.infoArr.push(new Info("David", 50));
390        })
391    }
392  }
393}
394```
395
396- When the entire object changes but the listened property remains unchanged, the \@Monitor callback is not triggered.
397
398The following code represents the behavior in the comment when you execute the instructions in the sequence of Step 1, Step 2 and Step 3.
399
400If you only execute the instruction of Step 2 or Step 3 to change the values of **name** or **age**, the **onNameChange** and **onAgeChange** methods are triggered.
401
402```ts
403@ObservedV2
404class Info {
405  @Trace person: Person;
406  @Monitor("person.name")
407  onNameChange(monitor: IMonitor) {
408    console.log(`name change from ${monitor.value()?.before} to ${monitor.value()?.now}`);
409  }
410  @Monitor("person.age")
411  onAgeChange(monitor: IMonitor) {
412    console.log(`age change from ${monitor.value()?.before} to ${monitor.value()?.now}`);
413  }
414  constructor(name: string, age: number) {
415    this.person = new Person(name, age);
416  }
417}
418@ObservedV2
419class Person {
420  @Trace name: string;
421  @Trace age: number;
422  constructor(name: string, age: number) {
423    this.name = name;
424    this.age = age;
425  }
426}
427@Entry
428@ComponentV2
429struct Index {
430  info: Info = new Info("Tom", 25);
431  build() {
432    Column() {
433      Button("Step 1, only change name")
434        .onClick(() => {
435          this.info.person = new Person("Jack", 25);  // Can trigger the onNameChange method, but not the onAgeChange method.
436        })
437      Button("Step 2, only change age")
438        .onClick(() => {
439          this.info.person = new Person("Jack", 18);  // Can trigger the onAgeChange method, but not the onNameChange method.
440        })
441      Button("Step 3, change name and age")
442        .onClick(() => {
443          this.info.person = new Person("Lucy", 19);  // Can trigger the onNameChange and onAgeChange methods.
444        })
445    }
446  }
447}
448```
449
450- If the property listened by \@Monitor is changed for multiple times in an event, the last change is used.
451
452```ts
453@ObservedV2
454class Frequence {
455  @Trace count: number = 0;
456  @Monitor("count")
457  onCountChange(monitor: IMonitor) {
458    console.log(`count change from ${monitor.value()?.before} to ${monitor.value()?.now}`);
459  }
460}
461@Entry
462@ComponentV2
463struct Index {
464  frequence: Frequence = new Frequence();
465  build() {
466    Column() {
467      Button("change count to 1000")
468        .onClick(() => {
469          for (let i = 1; i <= 1000; i++) {
470            this.frequence.count = i;
471          }
472        })
473      Button("change count to 0 then to 1000")
474        .onClick(() => {
475          for (let i = 999; i >= 0; i--) {
476            this.frequence.count = i;
477          }
478          this.frequence.count = 1000; // Cannot trigger the onCountChange method at last.
479        })
480    }
481  }
482}
483```
484
485After you click **change count to 1000**, the **onCountChange** method is triggered and the **count change from 0 to 1000** log is output. After you click **change count to 0 then to 1000**, the **onCountChange** method is not triggered because the value of **count** property remains 1000 before and after the event.
486
487## Constraints
488
489Pay attention to the following constraints when using \@Monitor:
490
491- Do not listen for the same property for multiple times in a class. When a property in a class is listened for multiple times, only the last listening method takes effect.
492
493```ts
494@ObservedV2
495class Info {
496  @Trace name: string = "Tom";
497  @Monitor("name")
498  onNameChange(monitor: IMonitor) {
499    console.log(`onNameChange`);
500  }
501  @Monitor("name")
502  onNameChangeDuplicate(monitor: IMonitor) {
503    console.log(`onNameChangeDuplicate`);
504  }
505}
506@Entry
507@ComponentV2
508struct Index {
509  info: Info = new Info();
510  build() {
511    Column() {
512      Button("change name")
513        .onClick(() => {
514          this.info.name = "Jack"; // Only the onNameChangeDuplicate method is triggered.
515        })
516    }
517  }
518}
519```
520
521- The \@Monitor parameter must be a string that listens for the property name. Only string literals, **const** constants, and **enum** enumerated values can be used as parameters. If a variable is used as a parameter, only the property corresponding to the variable value during \@Monitor initialization is listened. When a variable is changed, \@Monitor cannot change the listened property in real time. That is, the target property listened by \@Monitor is determined during initialization and cannot be dynamically changed. Do not use variables as \@Monitor parameters for initialization.
522
523```ts
524const t2: string = "t2"; // Constant
525enum ENUM {
526  T3 = "t3" // Enum
527};
528let t4: string = "t4"; // Variable
529@ObservedV2
530class Info {
531  @Trace t1: number = 0;
532  @Trace t2: number = 0;
533  @Trace t3: number = 0;
534  @Trace t4: number = 0;
535  @Trace t5: number = 0;
536  @Monitor("t1") // String literal
537  onT1Change(monitor: IMonitor) {
538    console.log(`t1 change from ${monitor.value()?.before} to ${monitor.value()?.now}`);
539  }
540  @Monitor(t2)
541  onT2Change(monitor: IMonitor) {
542    console.log(`t2 change from ${monitor.value()?.before} to ${monitor.value()?.now}`);
543  }
544  @Monitor(ENUM.T3)
545  onT3Change(monitor: IMonitor) {
546    console.log(`t3 change from ${monitor.value()?.before} to ${monitor.value()?.now}`);
547  }
548  @Monitor(t4)
549  onT4Change(monitor: IMonitor) {
550    console.log(`t4 change from ${monitor.value()?.before} to ${monitor.value()?.now}`);
551  }
552}
553@Entry
554@ComponentV2
555struct Index {
556  info: Info = new Info();
557  build() {
558    Column() {
559      Button("Change t1")
560        .onClick(() => {
561          this.info.t1++; // Can trigger the onT1Change method.
562        })
563      Button("Change t2")
564        .onClick(() => {
565          this.info.t2++; // Can trigger the onT2Change method.
566        })
567      Button("Change t3")
568        .onClick(() => {
569          this.info.t3++; // Can trigger the onT3Change method.
570        })
571      Button("Change t4")
572        .onClick(() => {
573          this.info.t4++; // Can trigger the onT4Change method.
574        })
575      Button("Change var t4 to t5")
576        .onClick(() => {
577          t4 = "t5"; // Change the variable value to "t5".
578        })
579      Button("Change t5")
580        .onClick(() => {
581          this.info.t5++; // The onT4Change still listens for t4. Cannot trigger the method.
582        })
583      Button("Change t4 again")
584        .onClick(() => {
585          this.info.t4++; // Can trigger the onT4Change method.
586        })
587    }
588  }
589}
590```
591
592- Changing the listened property in \@Monitor again may cause infinite loops, which is not recommended.
593
594```ts
595@ObservedV2
596class Info {
597  @Trace count: number = 0;
598  @Monitor("count")
599  onCountChange(monitor: IMonitor) {
600    this.count++; // Avoid using this method because it may cause infinite loops.
601  }
602}
603```
604
605## Comparing \@Monitor with \@Watch
606
607The following table compares the usage and functions of \@Monitor and \@Watch.
608
609|                    | \@Watch                                 | \@Monitor                                                    |
610| ------------------ | --------------------------------------- | ------------------------------------------------------------ |
611| Parameter              | Call back the method name.                             | Listen for the state variable name and property name.                                      |
612| Number of listened targets        | Only a single state variable can be listened.                   | Multiple state variables can be listened at the same time.                                      |
613| Listening capability          | Listen for the top-level state variables.           | Listen for the lower-level state variables.                                |
614| Obtain the value before change| No.                     | Yes.                                            |
615| Listening Condition          | The listened object is a state variable.                     | The listened object is a state variable or a class member property decorated by \@Trace.               |
616| Constraints          | It can be used only in custom components decorated by \@Component.| It can be used in custom components and classes decorated by \@ComponentV2.|
617
618## Use Scenarios
619
620### Listening for Lower-level Property Changes
621
622\@Monitor can listen for the lower-level property changes and classify them based on the values before and after the changes.
623
624In the following example, the change of property **value** is listened and the display style of the **Text** component is changed based on the change amplitude.
625
626```ts
627@ObservedV2
628class Info {
629  @Trace value: number = 50;
630}
631@ObservedV2
632class UIStyle {
633  info: Info = new Info();
634  @Trace color: Color = Color.Black;
635  @Trace fontSize: number = 45;
636  @Monitor("info.value")
637  onValueChange(monitor: IMonitor) {
638    let lastValue: number = monitor.value()?.before as number;
639    let curValue: number = monitor.value()?.now as number;
640    if (lastValue != 0) {
641      let diffPercent: number = (curValue - lastValue) / lastValue;
642      if (diffPercent > 0.1) {
643        this.color = Color.Red;
644        this.fontSize = 50;
645      } else if (diffPercent < -0.1) {
646        this.color = Color.Green;
647        this.fontSize = 40;
648      } else {
649        this.color = Color.Black;
650        this.fontSize = 45;
651      }
652    }
653  }
654}
655@Entry
656@ComponentV2
657struct Index {
658  textStyle: UIStyle = new UIStyle();
659  build() {
660    Column() {
661      Text(`Important Value: ${this.textStyle.info.value}`)
662        .fontColor(this.textStyle.color)
663        .fontSize(this.textStyle.fontSize)
664      Button("change!")
665        .onClick(() => {
666          this.textStyle.info.value = Math.floor(Math.random() * 100) + 1;
667        })
668    }
669  }
670}
671```
672
673## FAQs
674
675### Effective and Expiration Time of Variable Listening by the \@Monitor in the Custom Component
676
677When \@Monitor is defined in a custom component decorated by \@ComponentV2, \@Monitor takes effect after the state variable is initialized and becomes invalid when the component is destroyed.
678
679```ts
680@ObservedV2
681class Info {
682  @Trace message: string = "not initialized";
683
684  constructor() {
685    console.log("in constructor message change to initialized");
686    this.message = "initialized";
687  }
688}
689@ComponentV2
690struct Child {
691  @Param info: Info = new Info();
692  @Monitor("info.message")
693  onMessageChange(monitor: IMonitor) {
694    console.log(`Child message change from ${monitor.value()?.before} to ${monitor.value()?.now}`);
695  }
696  aboutToAppear(): void {
697    this.info.message = "Child aboutToAppear";
698  }
699  aboutToDisappear(): void {
700    console.log("Child aboutToDisappear");
701    this.info.message = "Child aboutToDisappear";
702  }
703  build() {
704    Column() {
705      Text("Child")
706      Button("change message in Child")
707        .onClick(() => {
708          this.info.message = "Child click to change Message";
709        })
710    }
711    .borderColor(Color.Red)
712    .borderWidth(2)
713
714  }
715}
716@Entry
717@ComponentV2
718struct Index {
719  @Local info: Info = new Info();
720  @Local flag: boolean = false;
721  @Monitor("info.message")
722  onMessageChange(monitor: IMonitor) {
723    console.log(`Index message change from ${monitor.value()?.before} to ${monitor.value()?.now}`);
724  }
725
726  build() {
727    Column() {
728      Button("show/hide Child")
729        .onClick(() => {
730          this.flag = !this.flag
731        })
732      Button("change message in Index")
733        .onClick(() => {
734          this.info.message = "Index click to change Message";
735        })
736      if (this.flag) {
737        Child({ info: this.info })
738      }
739    }
740  }
741}
742```
743
744In the preceding example, you can create and destroy a **Child** component to observe the effective and expiration time of the \@Monitor defined in the custom component. You are advised to follow the steps below:
745
746- When the **Index** component creates an instance of the **Info** class, the log outputs the message: **in constructor message change to initialized**. At this time, the \@Monitor of the **Index** component has not been initialized successfully, so \@Monitor cannot listen for the message change.
747- After the **Index** component is created and the page is loaded, click **change message in Index** button. \@Monitor now can listen for the change and the log outputs the message "Index message change from initialized to Index click to change Message".
748- Click the **show/hide Child** button to create a **Child** component. After this component initializes the \@Param decorated variables and \@Monitor, call the **aboutToAppear** callback of the **Child** component to change the message. In this case, the \@Monitor of the **Index** and **Child** components can listen for the change, and the logs outputs the messages "Index message change from Index click to change Message to Child aboutToAppear" and "Child message change from Index click to change Message to Child aboutToAppear."
749- Click **change message in Child** button to change the message. In this case, the \@Monitor of the **Index** and **Child** components can listen for the change, and the log outputs the messages "Index message change from Child aboutToAppear to Child click to change Message" and "Child message change from Child aboutToAppear to Child click to change Message."
750- Click the **show/hide Child** button to destroy the **Child** component and call the **aboutToDisappear** callback to change the message. In this case, the \@Monitor of the **Index** and **Child** components can listen for the change, and the log outputs the messages "Child aboutToDisappear, Index message change from Child click to change Message to Child aboutToDisappear", and "Child message change from Child click to change Message to Child aboutToDisappear."
751- Click **change message in Index** button to change the message. In this case, the **Child** component is destroyed, and the \@Monitor is deregistered. Only the \@Monitor of the **Index** component can listen for the changes and the log outputs the message "Index message change from Child aboutToDisappear to Index click to change Message."
752
753The preceding steps indicate that the \@Monitor defined in the **Child** component takes effect when the **Child** component is created and initialized, and becomes invalid when the **Child** component is destroyed.
754
755### Effective and Expiration Time of Variable Listening by the \@Monitor in the Class
756
757When \@Monitor is defined in a class decorated by \@ComponentV2, \@Monitor takes effect after the class is created and becomes invalid when the class is destroyed.
758
759```ts
760@ObservedV2
761class Info {
762  @Trace message: string = "not initialized";
763
764  constructor() {
765    this.message = "initialized";
766  }
767  @Monitor("message")
768  onMessageChange(monitor: IMonitor) {
769    console.log(`message change from ${monitor.value()?.before} to ${monitor.value()?.now}`);
770  }
771}
772
773@Entry
774@ComponentV2
775struct Index {
776  info: Info = new Info();
777
778  aboutToAppear(): void {
779    this.info.message = "Index aboutToAppear";
780  }
781
782  build() {
783    Column() {
784      Button("change message")
785        .onClick(() => {
786          this.info.message = "Index click to change message";
787        })
788    }
789  }
790}
791```
792
793In the preceding example, \@Monitor takes effect after the **info** class is created, which is later than the **constructor** of the class and earlier than the **aboutToAppear** of the custom component. After the page is loaded, click **change message** button to modify the message variable. The log outputs the messages as below:
794
795```ts
796message change from initialized to Index aboutToAppear
797message change from Index aboutToAppear to Index click to change message
798```
799
800\@Monitor defined in a class becomes invalid when the class is destroyed. However, the garbage collection mechanism determines whether a class is actually destroyed and released. Even if the custom component is destroyed, the class is not destroyed accordingly. As a result, the \@Monitor defined in the class still listens for changes.
801
802```ts
803@ObservedV2
804class InfoWrapper {
805  info?: Info;
806  constructor(info: Info) {
807    this.info = info;
808  }
809  @Monitor("info.age")
810  onInfoAgeChange(monitor: IMonitor) {
811    console.log(`age change from ${monitor.value()?.before} to ${monitor.value()?.now}`)
812  }
813}
814@ObservedV2
815class Info {
816  @Trace age: number;
817  constructor(age: number) {
818    this.age = age;
819  }
820}
821@ComponentV2
822struct Child {
823  @Param @Require infoWrapper: InfoWrapper;
824  aboutToDisappear(): void {
825    console.log("Child aboutToDisappear", this.infoWrapper.info?.age)
826  }
827  build() {
828    Column() {
829      Text(`${this.infoWrapper.info?.age}`)
830    }
831  }
832}
833@Entry
834@ComponentV2
835struct Index {
836  dataArray: Info[] = [];
837  @Local showFlag: boolean = true;
838  aboutToAppear(): void {
839    for (let i = 0; i < 5; i++) {
840      this.dataArray.push(new Info(i));
841    }
842  }
843  build() {
844    Column() {
845      Button("change showFlag")
846        .onClick(() => {
847          this.showFlag = !this.showFlag;
848        })
849      Button("change number")
850        .onClick(() => {
851          console.log("click to change age")
852          this.dataArray.forEach((info: Info) => {
853            info.age += 100;
854          })
855        })
856      if (this.showFlag) {
857        Column() {
858          Text("Childs")
859          ForEach(this.dataArray, (info: Info) => {
860            Child({ infoWrapper: new InfoWrapper(info) })
861          })
862        }
863        .borderColor(Color.Red)
864        .borderWidth(2)
865      }
866    }
867  }
868}
869```
870
871In the preceding example, when you click **change showFlag** to switch the condition of the **if** component, the **Child** component is destroyed. But when you click **change number** to change the value of **age**, the \@Monitor callback defined in **InfoWrapper** is still triggered. This is because the custom component **Child** has executed **aboutToDisappear**, but its member variable **infoWrapper** is not destroyed immediately. When the variable changes, the **onInfoAgeChange** method defined in **infoWrapper** can still be called, therefore, the \@Monitor callback is still triggered.
872
873The result is unstable when you use the garbage collection mechanism to cancel the listening of \@Monitor. You can use either of the following methods to manage the expiration time of the \@Monitor:
874
8751. Define \@Monitor in the custom component. When a custom component is destroyed, the state management framework cancels the listening of \@Monitor. Therefore, after the custom component calls **aboutToDisappear**, the \@Monitor callback will not be triggered even though the data of the custom component may not be released.
876
877```ts
878@ObservedV2
879class InfoWrapper {
880  info?: Info;
881  constructor(info: Info) {
882    this.info = info;
883  }
884}
885@ObservedV2
886class Info {
887  @Trace age: number;
888  constructor(age: number) {
889    this.age = age;
890  }
891}
892@ComponentV2
893struct Child {
894  @Param @Require infoWrapper: InfoWrapper;
895  @Monitor("infoWrapper.info.age")
896  onInfoAgeChange(monitor: IMonitor) {
897    console.log(`age change from ${monitor.value()?.before} to ${monitor.value()?.now}`)
898  }
899  aboutToDisappear(): void {
900    console.log("Child aboutToDisappear", this.infoWrapper.info?.age)
901  }
902  build() {
903    Column() {
904      Text(`${this.infoWrapper.info?.age}`)
905    }
906  }
907}
908@Entry
909@ComponentV2
910struct Index {
911  dataArray: Info[] = [];
912  @Local showFlag: boolean = true;
913  aboutToAppear(): void {
914    for (let i = 0; i < 5; i++) {
915      this.dataArray.push(new Info(i));
916    }
917  }
918  build() {
919    Column() {
920      Button("change showFlag")
921        .onClick(() => {
922          this.showFlag = !this.showFlag;
923        })
924      Button("change number")
925        .onClick(() => {
926          console.log("click to change age")
927          this.dataArray.forEach((info: Info) => {
928            info.age += 100;
929          })
930        })
931      if (this.showFlag) {
932        Column() {
933          Text("Childs")
934          ForEach(this.dataArray, (info: Info) => {
935            Child({ infoWrapper: new InfoWrapper(info) })
936          })
937        }
938        .borderColor(Color.Red)
939        .borderWidth(2)
940      }
941    }
942  }
943}
944```
945
9462. Set the listened object to empty. When the custom component is about to be destroyed, the \@Monitor listened object is set empty. In this way, the \@Monitor cannot listen for the changes of the original object, so that the listening is cancelled.
947
948```ts
949@ObservedV2
950class InfoWrapper {
951  info?: Info;
952  constructor(info: Info) {
953    this.info = info;
954  }
955  @Monitor("info.age")
956  onInfoAgeChange(monitor: IMonitor) {
957    console.log(`age change from ${monitor.value()?.before} to ${monitor.value()?.now}`)
958  }
959}
960@ObservedV2
961class Info {
962  @Trace age: number;
963  constructor(age: number) {
964    this.age = age;
965  }
966}
967@ComponentV2
968struct Child {
969  @Param @Require infoWrapper: InfoWrapper;
970  aboutToDisappear(): void {
971    console.log("Child aboutToDisappear", this.infoWrapper.info?.age)
972    this.infoWrapper.info = undefined; // Disable the InfoWrapper from listening for info.age.
973  }
974  build() {
975    Column() {
976      Text(`${this.infoWrapper.info?.age}`)
977    }
978  }
979}
980@Entry
981@ComponentV2
982struct Index {
983  dataArray: Info[] = [];
984  @Local showFlag: boolean = true;
985  aboutToAppear(): void {
986    for (let i = 0; i < 5; i++) {
987      this.dataArray.push(new Info(i));
988    }
989  }
990  build() {
991    Column() {
992      Button("change showFlag")
993        .onClick(() => {
994          this.showFlag = !this.showFlag;
995        })
996      Button("change number")
997        .onClick(() => {
998          console.log("click to change age")
999          this.dataArray.forEach((info: Info) => {
1000            info.age += 100;
1001          })
1002        })
1003      if (this.showFlag) {
1004        Column() {
1005          Text("Childs")
1006          ForEach(this.dataArray, (info: Info) => {
1007            Child({ infoWrapper: new InfoWrapper(info) })
1008          })
1009        }
1010        .borderColor(Color.Red)
1011        .borderWidth(2)
1012      }
1013    }
1014  }
1015}
1016```
1017
1018### Passing Correct Input Parameters to \@Monitor
1019
1020\@Monitor cannot verify input parameters during compilation. Currently, the following statements do not meet the listening condition, but \@Monitor is still triggered. Therefore, you should correctly pass the input parameter and do not pass non-state variables. Otherwise, function exceptions or unexpected behavior may occur.
1021
1022[Negative example 1]
1023
1024```ts
1025@ObservedV2
1026class Info {
1027  name: string = "John";
1028  @Trace age: number = 24;
1029  @Monitor("age", "name") // Listen for the state variable "age" and non-state variable "name" at the same time.
1030  onPropertyChange(monitor: IMonitor) {
1031    monitor.dirty.forEach((path: string) => {
1032      console.log(`property path:${path} change from ${monitor.value(path)?.before} to ${monitor.value(path)?.now}`);
1033    })
1034  }
1035}
1036@Entry
1037@ComponentV2
1038struct Index {
1039  info: Info = new Info();
1040  build() {
1041    Column() {
1042      Button("change age&name")
1043        .onClick(() => {
1044          this.info.age = 25; // Change the state variable "age" and non-state variable "name" at the same time.
1045          this.info.name = "Johny";
1046        })
1047    }
1048  }
1049}
1050```
1051
1052In the preceding code, when state variable **age** and non-state variable **name** are changed at the same time, the following log is generated:
1053
1054```
1055property path:age change from 24 to 25
1056property path:name change from John to Johny
1057```
1058
1059Actually, the **name** attribute is not an observable variable and should not be added to the input parameters of \@Monitor. You are advised to remove the listening from the **name** attribute or add \@Trace to decorate the **name** attribute as a state variable.
1060
1061[Correct example 1]
1062
1063```ts
1064@ObservedV2
1065class Info {
1066  name: string = "John";
1067  @Trace age: number = 24;
1068  @Monitor("age") // Only listen for the state variable "age".
1069  onPropertyChange(monitor: IMonitor) {
1070    monitor.dirty.forEach((path: string) => {
1071      console.log(`property path:${path} change from ${monitor.value(path)?.before} to ${monitor.value(path)?.now}`);
1072    })
1073  }
1074}
1075@Entry
1076@ComponentV2
1077struct Index {
1078  info: Info = new Info();
1079  build() {
1080    Column() {
1081      Button("change age&name")
1082        .onClick(() => {
1083          this.info.age = 25; // The state variable "age" is changed.
1084          this.info.name = "Johny";
1085        })
1086    }
1087  }
1088}
1089```
1090
1091[Negative example 2]
1092
1093```ts
1094@ObservedV2
1095class Info {
1096  name: string = "John";
1097  @Trace age: number = 24;
1098  get myAge() {
1099    return this.age; // age is a state variable.
1100  }
1101  @Monitor("myAge") // Listen for non-@Computed decorated getter accessor.
1102  onPropertyChange() {
1103    console.log("age changed");
1104  }
1105}
1106@Entry
1107@ComponentV2
1108struct Index {
1109  info: Info = new Info();
1110  build() {
1111    Column() {
1112      Button("change age")
1113        .onClick(() => {
1114          this.info.age = 25; // The state variable "age" is changed.
1115        })
1116    }
1117  }
1118}
1119```
1120
1121In the preceding code, the input parameter of \@Monitor is the name of a **getter** accessor. This accessor is not decorated by \@Computed and is not a variable that can be listened for. However, the state variable is used for computation. After the state variable changes, **myAge** is changed, invoking the \@Monitor callback. You are advised to add an \@Computed decorator to **myAge** or directly listen for the state variable itself when the **getter** accessor returns the state variable.
1122
1123[Positive example 2]
1124
1125Change **myAge** to a state variable:
1126
1127```ts
1128@ObservedV2
1129class Info {
1130  name: string = "John";
1131  @Trace age: number = 24;
1132  @Computed // Add @Computed to myAge as a state variable.
1133  get myAge() {
1134    return this.age;
1135  }
1136  @Monitor("myAge") // Listen for @Computed decorated getter accessor.
1137  onPropertyChange() {
1138    console.log("age changed");
1139  }
1140}
1141@Entry
1142@ComponentV2
1143struct Index {
1144  info: Info = new Info();
1145  build() {
1146    Column() {
1147      Button("change age")
1148        .onClick(() => {
1149          this.info.age = 25; // The state variable "age" is changed.
1150        })
1151    }
1152  }
1153}
1154```
1155
1156Alternatively, listen to the state variable itself.
1157
1158```ts
1159@ObservedV2
1160class Info {
1161  name: string = "John";
1162  @Trace age: number = 24;
1163  @Monitor("age") // Only listen for the state variable "age".
1164  onPropertyChange() {
1165    console.log("age changed");
1166  }
1167}
1168@Entry
1169@ComponentV2
1170struct Index {
1171  info: Info = new Info();
1172  build() {
1173    Column() {
1174      Button("change age")
1175        .onClick(() => {
1176          this.info.age = 25; // The state variable "age" is changed.
1177        })
1178    }
1179  }
1180}
1181```
1182