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>State management V2 is still under development, and some features may be incomplete or not always work as expected.
10
11## Overview
12
13To listen for value changes of the state variables in a lower level, you can use the \@Monitor decorator:
14
15- 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).
16
17- 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.
18- 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.
19- 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.
20- 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.
21- 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.
22- 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 the comparison between \@Monitor and \@Watch, see [Comparing \@Monitor with \@Watch](#comparing-\@Monitor-with-\@Watch).
23
24## Limitations of the \@Watch decorator in State Management V1
25
26This 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.
27
28```ts
29@Observed
30class Info {
31  name: string = "Tom";
32  age: number = 25;
33}
34@Entry
35@Component
36struct Index {
37  @State @Watch('onInfoChange') info: Info = new Info();
38  @State @Watch('onNumArrChange') numArr: number[] = [1,2,3,4,5];
39
40  onInfoChange() {
41    console.log(`info after change name: ${this.info.name}, age: ${this.info.age} `);
42  }
43  onNumArrChange() {
44    console.log(`numArr after change ${JSON.stringify(this.numArr)}`);
45  }
46  build() {
47    Row() {
48      Column() {
49        Button("change info name")
50          .onClick(() => {
51            this.info.name = "Jack";
52          })
53        Button("change info age")
54          .onClick(() => {
55            this.info.age = 30;
56          })
57        Button("change numArr[2]")
58          .onClick(() => {
59            this.numArr[2] = 5;
60          })
61        Button("change numArr[3]")
62          .onClick(() => {
63            this.numArr[3] = 6;
64          })
65      }
66      .width('100%')
67    }
68    .height('100%')
69  }
70}
71```
72
73In 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.
74
75## Decorator Description
76
77| \@Monitor Property Decorator| Description                                                        |
78| ------------------- | ------------------------------------------------------------ |
79| 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).|
80| 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.|
81
82## API Description
83
84### IMonitor Type
85
86Variables of the IMonitor type are used as parameters for \@Monitor to decorate a method.
87
88| Property      | Type           | Parameter         | Return Value            | Description                                                        |
89| ---------- | --------------- | ------------- | ------------------ | ------------------------------------------------------------ |
90| dirty      | Array\<string\> | None.           | None.                | Saves the changed property name.                                      |
91| 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.|
92
93### IMonitorValue\<T\> Type
94
95Saves the information about property changes, including the property name, original value, and new value.
96
97| Property  | Type  | Description                      |
98| ------ | ------ | -------------------------- |
99| before | T      | Listens for the value before the property change.    |
100| now    | T      | Listens for the current value after the property changes.|
101| path   | string | Listened property name.            |
102
103## Listened Changes
104
105### Using \@Monitor in Custom Components Decorated by \@ComponentV2
106
107When the state variables listened by \@Monitor change, the callback is triggered.
108
109- 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 (,).
110
111  ```ts
112  @Entry
113  @ComponentV2
114  struct Index {
115    @Local message: string = "Hello World";
116    @Local name: string = "Tom";
117    @Local age: number = 24;
118    @Monitor("message", "name")
119    onStrChange(monitor: IMonitor) {
120      monitor.dirty.forEach((path: string) => {
121        console.log(`${path} changed from ${monitor.value(path)?.before} to ${monitor.value(path)?.now}`)
122      })
123    }
124    build() {
125      Column() {
126        Button("change string")
127          .onClick(() => {
128            this.message += "!";
129            this.name = "Jack";
130        })
131      }
132    }
133  }
134  ```
135
136- 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.
137
138  ```ts
139  class Info {
140    name: string;
141    age: number;
142    constructor(name: string, age: number) {
143      this.name = name;
144      this.age = age;
145    }
146  }
147  @Entry
148  @ComponentV2
149  struct Index {
150    @Local info: Info = new Info("Tom", 25);
151    @Monitor("info")
152    infoChange(monitor: IMonitor) {
153      console.log(`info change`);
154    }
155    @Monitor("info.name")
156    infoPropertyChange(monitor: IMonitor) {
157      console.log(`info name change`);
158    }
159    build() {
160      Column() {
161        Text(`name: ${this.info.name}, age: ${this.info.age}`)
162        Button("change info")
163          .onClick(() => {
164            this.info = new Info ("Lucy", 18); // Can listen for the change.
165          })
166        Button("change info.name")
167          .onClick(() => {
168            this.info.name = "Jack"; // Cannot listen for the change.
169          })
170      }
171    }
172  }
173  ```
174
175### Using \@Monitor in Classes Decorated by \@ObservedV2
176
177When the properties listened by \@Monitor change, the callback is triggered.
178
179- 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 (,).
180
181```ts
182@ObservedV2
183class Info {
184  @Trace name: string = "Tom";
185  @Trace region: string = "North";
186  @Trace job: string = "Teacher";
187  age: number = 25;
188  // name is decorated by @Trace. Can listen for the change.
189  @Monitor("name")
190  onNameChange(monitor: IMonitor) {
191    console.log(`name change from ${monitor.value()?.before} to ${monitor.value()?.now}`);
192  }
193  // age is not decorated by @Trace. Cannot listen for the change.
194  @Monitor("age")
195  onAgeChange(monitor: IMonitor) {
196    console.log(`age change from ${monitor.value()?.before} to ${monitor.value()?.now}`);
197  }
198  // region and job are decorated by @Trace. Can listen for the change.
199  @Monitor("region", "job")
200  onChange(monitor: IMonitor) {
201    monitor.dirty.forEach((path: string) => {
202      console.log(`${path} change from ${monitor.value(path)?.before} to ${monitor.value(path)?.now}`);
203    })
204  }
205}
206@Entry
207@ComponentV2
208struct Index {
209  info: Info = new Info();
210  build() {
211    Column() {
212      Button("change name")
213        .onClick(() => {
214          this.info.name = "Jack"; // Can trigger the onNameChange method.
215        })
216      Button("change age")
217        .onClick(() => {
218          this.info.age = 26; // Cannot trigger the onAgeChange method.
219        })
220      Button("change region")
221        .onClick(() => {
222          this.info.region = "South"; // Can trigger the onChange method.
223        })
224      Button("change job")
225        .onClick(() => {
226          this.info.job = "Driver"; // Can trigger the onChange method.
227        })
228    }
229  }
230}
231```
232
233- \@Monitor can listen for the changes of lower-level properties which should be decorated by @Trace.
234
235```ts
236@ObservedV2
237class Inner {
238  @Trace num: number = 0;
239}
240@ObservedV2
241class Outer {
242  inner: Inner = new Inner();
243  @Monitor("inner.num")
244  onChange(monitor: IMonitor) {
245    console.log(`inner.num change from ${monitor.value()?.before} to ${monitor.value()?.now}`);
246  }
247}
248@Entry
249@ComponentV2
250struct Index {
251  outer: Outer = new Outer();
252  build() {
253    Column() {
254      Button("change name")
255        .onClick(() => {
256          this.outer.inner.num = 100; // Can trigger the onChange method.
257        })
258    }
259  }
260}
261```
262
263- In the inheritance class scenario, you can listen for the same property for multiple times in the inheritance chain.
264
265```ts
266@ObservedV2
267class Base {
268  @Trace name: string;
269  // Listen for the name property of the base class.
270  @Monitor("name")
271  onBaseNameChange(monitor: IMonitor) {
272    console.log(`Base Class name change`);
273  }
274  constructor(name: string) {
275    this.name = name;
276  }
277}
278@ObservedV2
279class Derived extends Base {
280  // Listen for the name property of the inheritance class.
281  @Monitor("name")
282  onDerivedNameChange(monitor: IMonitor) {
283    console.log(`Derived Class name change`);
284  }
285  constructor(name: string) {
286    super(name);
287  }
288}
289@Entry
290@ComponentV2
291struct Index {
292  derived: Derived = new Derived("AAA");
293  build() {
294    Column() {
295      Button("change name")
296        .onClick(() => {
297          this.derived.name = "BBB"; // Can trigger the onBaseNameChange and onDerivedNameChange methods in sequence.
298        })
299    }
300  }
301}
302```
303
304### General Listening Capability
305
306\@Monitor also has some general listening capabilities.
307
308- \@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.
309
310```ts
311@ObservedV2
312class Info {
313  @Trace name: string;
314  @Trace age: number;
315  
316  constructor(name: string, age: number) {
317    this.name = name;
318    this.age = age;
319  }
320}
321@ObservedV2
322class ArrMonitor {
323  @Trace dimensionTwo: number[][] = [[1,1,1],[2,2,2],[3,3,3]];
324  @Trace dimensionThree: number[][][] = [[[1],[2],[3]],[[4],[5],[6]],[[7],[8],[9]]];
325  @Trace infoArr: Info[] = [new Info("Jack", 24), new Info("Lucy", 18)];
326  // dimensionTwo is a two-dimensional simple array and is decorated by @Trace. Can observe the element changes.
327  @Monitor("dimensionTwo.0.0", "dimensionTwo.1.1")
328  onDimensionTwoChange(monitor: IMonitor) {
329    monitor.dirty.forEach((path: string) => {
330      console.log(`dimensionTwo path: ${path} change from ${monitor.value(path)?.before} to ${monitor.value(path)?.now}`);
331    })
332  }
333  // dimensionThree is a three-dimensional simple array and is decorated by @Trace. Can observe the element changes.
334  @Monitor("dimensionThree.0.0.0", "dimensionThree.1.1.0")
335  onDimensionThreeChange(monitor: IMonitor) {
336    monitor.dirty.forEach((path: string) => {
337      console.log(`dimensionThree path: ${path} change from ${monitor.value(path)?.before} to ${monitor.value(path)?.now}`);
338    })
339  }
340  // name and age properties of the info class are decorated by @Trace. Can listen for the changes.
341  @Monitor("infoArr.0.name", "infoArr.1.age")
342  onInfoArrPropertyChange(monitor: IMonitor) {
343    monitor.dirty.forEach((path: string) => {
344      console.log(`infoArr path:${path} change from ${monitor.value(path)?.before} to ${monitor.value(path)?.now}`);
345    })
346  }
347  // infoArr is decorated by @Trace. Can listen for the value changes.
348  @Monitor("infoArr")
349  onInfoArrChange(monitor: IMonitor) {
350    console.log(`infoArr whole change`);
351  }
352  // Can listen for the length change of the infoArr.
353  @Monitor("infoArr.length")
354  onInfoArrLengthChange(monitor: IMonitor) {
355    console.log(`infoArr length change`);
356  }
357}
358@Entry
359@ComponentV2
360struct Index {
361  arrMonitor: ArrMonitor = new ArrMonitor();
362  build() {
363    Column() {
364      Button("Change dimensionTwo")
365        .onClick(() => {
366          // Can trigger the onDimensionTwoChange method. 
367          this.arrMonitor.dimensionTwo[0][0]++; 
368          this.arrMonitor.dimensionTwo[1][1]++; 
369        })
370      Button("Change dimensionThree")
371        .onClick(() => {
372          // Can trigger the onDimensionThreeChange method.
373          this.arrMonitor.dimensionThree[0][0][0]++;
374          this.arrMonitor.dimensionThree[1][1][0]++; 
375        })
376      Button("Change info property")
377        .onClick(() => {
378          // Can trigger the onInfoArrPropertyChange method.
379          this.arrMonitor.infoArr[0].name = "Tom"; 
380          this.arrMonitor.infoArr[1].age = 19; 
381        })
382      Button("Change whole infoArr")
383        .onClick(() => {
384          // Can trigger the onInfoArrChange, onInfoArrPropertyChange, and onInfoArrLengthChange methods.
385          this.arrMonitor.infoArr = [new Info("Cindy", 8)]; 
386        })
387      Button("Push new info to infoArr")
388        .onClick(() => {
389          // Can trigger the onInfoArrPropertyChange and onInfoArrLengthChange methods.
390          this.arrMonitor.infoArr.push(new Info("David", 50)); 
391        })
392    }
393  }
394}
395```
396
397- When the entire object changes but the listened property remains unchanged, the \@Monitor callback is not triggered.
398
399The following code represents the behavior in the comment when you execute the instructions in the sequence of Step 1, Step 2 and Step 3.
400
401If 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.
402
403```ts
404@ObservedV2
405class Info {
406  @Trace person: Person;
407  @Monitor("person.name")
408  onNameChange(monitor: IMonitor) {
409    console.log(`name change from ${monitor.value()?.before} to ${monitor.value()?.now}`);
410  }
411  @Monitor("person.age")
412  onAgeChange(monitor: IMonitor) {
413    console.log(`age change from ${monitor.value()?.before} to ${monitor.value()?.now}`);
414  }
415  constructor(name: string, age: number) {
416    this.person = new Person(name, age);
417  }
418}
419@ObservedV2
420class Person {
421  @Trace name: string;
422  @Trace age: number;
423  constructor(name: string, age: number) {
424    this.name = name;
425    this.age = age;
426  }
427}
428@Entry
429@ComponentV2
430struct Index {
431  info: Info = new Info("Tom", 25);
432  build() {
433    Column() {
434      Button("Step 1, only change name")
435        .onClick(() => {
436          this.info.person = new Person("Jack", 25);  // Can trigger the onNameChange method, but not the onAgeChange method.
437        })
438      Button("Step 2, only change age")
439        .onClick(() => {
440          this.info.person = new Person("Jack", 18);  // Can trigger the onAgeChange method, but not the onNameChange method.
441        })
442      Button("Step 3, change name and age")
443        .onClick(() => {
444          this.info.person = new Person("Lucy", 19);  // Can trigger the onNameChange and onAgeChange methods.
445        })
446    }
447  }
448}
449```
450
451- If the property listened by \@Monitor is changed for multiple times in an event, the last change is used.
452
453```ts
454@ObservedV2
455class Frequence {
456  @Trace count: number = 0;
457  @Monitor("count")
458  onCountChange(monitor: IMonitor) {
459    console.log(`count change from ${monitor.value()?.before} to ${monitor.value()?.now}`);
460  }
461}
462@Entry
463@ComponentV2
464struct Index {
465  frequence: Frequence = new Frequence();
466  build() {
467    Column() {
468      Button("change count to 1000")
469        .onClick(() => {
470          for (let i = 1; i <= 1000; i++) {
471            this.frequence.count = i;
472          }
473        })
474      Button("change count to 0 then to 1000")
475        .onClick(() => {
476          for (let i = 999; i >= 0; i--) {
477            this.frequence.count = i;
478          }
479          this.frequence.count = 1000; // Cannot trigger the onCountChange method at last.
480        })
481    }
482  }
483}
484```
485
486After 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.
487
488## Constraints
489
490Pay attention to the following constraints when using \@Monitor:
491
492- 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.
493
494```ts
495@ObservedV2
496class Info {
497  @Trace name: string = "Tom";
498  @Monitor("name")
499  onNameChange(monitor: IMonitor) {
500    console.log(`onNameChange`);
501  }
502  @Monitor("name")
503  onNameChangeDuplicate(monitor: IMonitor) {
504    console.log(`onNameChangeDuplicate`);
505  }
506}
507@Entry
508@ComponentV2
509struct Index {
510  info: Info = new Info();
511  build() {
512    Column() {
513      Button("change name")
514        .onClick(() => {
515          this.info.name = "Jack"; // Only the onNameChangeDuplicate method is triggered.
516        })
517    }
518  }
519}
520```
521
522- 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.
523
524```ts
525const t2: string = "t2"; // Constant
526enum ENUM {
527  Enumerated Values of T3 = "t3" // Enumerated value
528};
529let t4: string = "t4"; // Variable
530@ObservedV2
531class Info {
532  @Trace t1: number = 0;
533  @Trace t2: number = 0;
534  @Trace t3: number = 0;
535  @Trace t4: number = 0;
536  @Trace t5: number = 0;
537  @Monitor("t1") // String literal
538  onT1Change(monitor: IMonitor) {
539    console.log(`t1 change from ${monitor.value()?.before} to ${monitor.value()?.now}`);
540  }
541  @Monitor(t2)
542  onT2Change(monitor: IMonitor) {
543    console.log(`t2 change from ${monitor.value()?.before} to ${monitor.value()?.now}`);
544  }
545  @Monitor(ENUM.T3)
546  onT3Change(monitor: IMonitor) {
547    console.log(`t3 change from ${monitor.value()?.before} to ${monitor.value()?.now}`);
548  }
549  @Monitor(t4)
550  onT4Change(monitor: IMonitor) {
551    console.log(`t4 change from ${monitor.value()?.before} to ${monitor.value()?.now}`);
552  }
553}
554@Entry
555@ComponentV2
556struct Index {
557  info: Info = new Info();
558  build() {
559    Column() {
560      Button("Change t1")
561        .onClick(() => {
562          this.info.t1++; // Can trigger the onT1Change method.
563        })
564      Button("Change t2")
565        .onClick(() => {
566          this.info.t2++; // Can trigger the onT2Change method.
567        })
568      Button("Change t3")
569        .onClick(() => {
570          this.info.t3++; // Can trigger the onT3Change method.
571        })
572      Button("Change t4")
573        .onClick(() => {
574          this.info.t4++; // Can trigger the onT4Change method.
575        })
576      Button("Change var t4 to t5")
577        .onClick(() => {
578          t4 = "t5"; // Change the variable value to "t5".
579        })
580      Button("Change t5")
581        .onClick(() => {
582          this.info.t5++; // The onT4Change still listens for t4. Cannot trigger the method.
583        })
584      Button("Change t4 again")
585        .onClick(() => {
586          this.info.t4++; // Can trigger the onT4Change method.
587        })
588    }
589  }
590}
591```
592
593- Changing the listened property in \@Monitor again may cause infinite loops, which is not recommended.
594
595```ts
596@ObservedV2
597class Info {
598  @Trace count: number = 0;
599  @Monitor("count")
600  onCountChange(monitor: IMonitor) {
601    this.count++; // Avoid using this method because it may cause infinite loops.
602  }
603}
604```
605
606## Comparing \@Monitor with \@Watch
607
608The following table compares the usage and functions of \@Monitor and \@Watch.
609
610|                    | \@Watch                                 | \@Monitor                                                    |
611| ------------------ | --------------------------------------- | ------------------------------------------------------------ |
612| Parameter              | Call back the method name.                             | Listen for the state variable name and property name.                                      |
613| Number of listened targets        | Only a single state variable can be listened.                   | Multiple state variables can be listened at the same time.                                      |
614| Listening capability          | Listen for the top-level state variables.           | Listen for the lower-level state variables.                                |
615| Obtain the value before change| No.                     | Yes.                                            |
616| Listening Condition          | The listened object is a state variable.                     | The listened object is a state variable or a class member property decorated by \@Trace.               |
617| Constraints          | It can be used only in custom components decorated by \@Component.| It can be used in custom components and classes decorated by \@ComponentV2.|
618
619## Use Scenarios
620
621### Listening for Lower-level Property Changes
622
623\@Monitor can listen for the lower-level property changes and classify them based on the values before and after the changes.
624
625In 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.
626
627```ts
628@ObservedV2
629class Info {
630  @Trace value: number = 50;
631}
632@ObservedV2
633class UIStyle {
634  info: Info = new Info();
635  @Trace color: Color = Color.Black;
636  @Trace fontSize: number = 45;
637  @Monitor("info.value")
638  onValueChange(monitor: IMonitor) {
639    let lastValue: number = monitor.value()?.before as number;
640    let curValue: number = monitor.value()?.now as number;
641    if (lastValue != 0) {
642      let diffPercent: number = (curValue - lastValue) / lastValue;
643      if (diffPercent > 0.1) {
644        this.color = Color.Red;
645        this.fontSize = 50;
646      } else if (diffPercent < -0.1) {
647        this.color = Color.Green;
648        this.fontSize = 40;
649      } else {
650        this.color = Color.Black;
651        this.fontSize = 45;
652      }
653    }
654  }
655}
656@Entry
657@ComponentV2
658struct Index {
659  textStyle: UIStyle = new UIStyle();
660  build() {
661    Column() {
662      Text(`Important Value: ${this.textStyle.info.value}`)
663        .fontColor(this.textStyle.color)
664        .fontSize(this.textStyle.fontSize)
665      Button("change!")
666        .onClick(() => {
667          this.textStyle.info.value = Math.floor(Math.random() * 100) + 1;
668        })
669    }
670  }
671}
672```
673