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