1# \@State Decorator: State Owned by Component 2 3 4An \@State decorated variable, also called a state variable, is a variable that holds the state property and is used to render the owning custom component. When it changes, the UI is re-rendered accordingly. 5 6 7Among the decorators related to state variables, \@State is the most basic decorator, as it is the one that empowers variables to have the state property. It is also the data source of most state variables. 8 9Before reading this topic, you are advised to read [State Management Overview](./arkts-state-management-overview.md). 10 11> **NOTE** 12> 13> This decorator can be used in ArkTS widgets since API version 9. 14> 15> This decorator can be used in atomic services since API version 11. 16 17## Overview 18 19An @State decorated variable, like all other decorated variables in the declarative paradigm, are private and only accessible from within the component. Its type and its local initialization must be specified. Initialization from the parent component using the named parameter mechanism is accepted. 20 21\@State decorated variables have the following features: 22 23- A one-way synchronization relationship can be set up from an \@State decorated variable to an \@Prop decorated variable in a child component, and a two-way synchronization relationship to an \@Link or \@ObjectLink decorated variable. 24 25- The lifecycle of the \@State decorated variable is the same as that of its owning custom component. 26 27 28## Rules of Use 29 30| \@State Decorator | Description | 31| ------------------ | ------------------------------------------------------------ | 32| Decorator parameters | None. | 33| Synchronization type | Does not synchronize with any type of variable in the parent component. | 34| Allowed variable types| Object, class, string, number, Boolean, enum, and array of these types.<br>Date type.<br>(Applicable to API version 11 or later) [Map](#decorating-variables-of-the-map-type) or [Set](#decorating-variables-of-the-set-type) type.<br>**undefined** or **null**.<br>Union types defined by the ArkUI framework, for example, [Length](../reference/apis-arkui/arkui-ts/ts-types.md#length), [ResourceStr](../reference/apis-arkui/arkui-ts/ts-types.md#resourcestr) and [ResourceColor](../reference/apis-arkui/arkui-ts/ts-types.md#resourcecolor).<br>The type must be specified.<br>For details about the scenarios of supported types, see [Observed Changes](#observed-changes).<br>**any** is not supported.<br>(Applicable to API version 11 or later) Union type of the preceding types, for example, **string \| number**, **string \| undefined**, or **ClassA \| null**. For details, see [Union Type](#union-type).<br>**NOTE**<br>When **undefined** or **null** is used, you are advised to explicitly specify the type to pass the TypeScript type check. For example, **@State a: string \| undefined = undefined** is supported, but **@Prop a: string = undefined** is not.| 35| Initial value for the decorated variable| Local initialization is required. | 36 37 38## Variable Transfer/Access Rules 39 40| Transfer/Access | Description | 41| ------------------ | ------------------------------------------------------------ | 42| Initialization from the parent component | Optional. Initialization from the parent component or local initialization can be used. For the former one, if the value passed from the parent component is not **undefined**, the local initialization is overwritten. Otherwise, the initial value of the @State decorated variable is used.<br>Supports normal variables (value changes to the @State by normal variables trigger only initialization. Changes to the state variables can trigger UI re-rendering), \@State, [\@Link](arkts-link.md), [\@Prop](arkts-prop.md), [\@Provide](arkts-provide-and-consume.md), [\@Consume](arkts-provide-and-consume.md), [\@ObjectLink](arkts-observed-and-objectlink.md), [\@StorageLink](arkts-appstorage.md#storagelink), [\@StorageProp](arkts-appstorage.md#storageprop), and [\@LocalStorageLink](arkts-localstorage.md#localstoragelink) and [\@LocalStorageProp](arkts-localstorage.md#localstorageprop) decorated variables in the parent component to initialize the \@State of the child component.| 43| Child component initialization | Supported. An \@State decorated variable can be used to initialize a regular variable or \@State, \@Link, \@Prop, or \@Provide decorated variable in the child component.| 44| Access from outside the component| Private, accessible only within the component. | 45 46 **Figure 1** Initialization rule 47 48 49 50 51## Observed Changes and Behavior 52 53Not all changes to state variables cause UI updates. Only changes that can be observed by the framework do. This section describes what changes can be observed and how the framework triggers UI re-renders after the changes are observed, that is, how the framework behaves. 54 55 56### Observed Changes 57 58- When the decorated variable is of the Boolean, string, or number type, its value change can be observed. 59 60 ```ts 61 // Simple type. 62 @State count: number = 0; 63 // The value change can be observed. 64 this.count = 1; 65 ``` 66 67- When the decorated variable is of the class or Object type, its value change and value changes of all its properties, that is, the properties that **Object.keys(observedObject)** returns, can be observed. Below are some examples: 68 69 Declare the **Person** and **Model** classes. 70 71 ```ts 72 class Person { 73 public value: string; 74 75 constructor(value: string) { 76 this.value = value; 77 } 78 } 79 80 class Model { 81 public value: string; 82 public name: Person; 83 constructor(value: string, person: Person) { 84 this.value = value; 85 this.name = person; 86 } 87 } 88 ``` 89 90 Use \@State to decorate a variable of the Model class object type. 91 92 ```ts 93 // Class type 94 @State title: Model = new Model('Hello', new Person('World')); 95 ``` 96 97 Assign a value to the \@State decorated variable. 98 99 ```ts 100 // Assign a value to the class object. 101 this.title = new Model('Hi', new Person('ArkUI')); 102 ``` 103 104 Assign a value to a property of the \@State decorated variable. 105 106 ```ts 107 // Assign a value to a property of the class object. 108 this.title.value = 'Hi'; 109 ``` 110 111 The value assignment of the nested property cannot be observed. 112 113 ```ts 114 // The value assignment of the nested property cannot be observed. 115 this.title.name.value = 'ArkUI'; 116 ``` 117- When the decorated variable is of the array type, the addition, deletion, and updates of array items can be observed. Below are some examples: 118 Declare the **Model** class. 119 120 ```ts 121 class Model { 122 public value: number; 123 constructor(value: number) { 124 this.value = value; 125 } 126 } 127 ``` 128 129 Use \@State to decorate a variable of the Model class array type. 130 131 ```ts 132 // Array type 133 @State title: Model[] = [new Model(11), new Model(1)]; 134 ``` 135 136 The value assignment of the array itself can be observed. 137 138 ```ts 139 // Value assignment of the array 140 this.title = [new Model(2)]; 141 ``` 142 143 The value assignment of array items can be observed. 144 145 ```ts 146 // Value assignment of an array item 147 this.title[0] = new Model(2); 148 ``` 149 150 The deletion of array items can be observed. 151 152 ```ts 153 // Array item change 154 this.title.pop(); 155 ``` 156 157 The addition of array items can be observed. 158 159 ```ts 160 // Array item change 161 this.title.push(new Model(12)); 162 ``` 163 164 The property value assignment in the array items cannot be observed. 165 166 ```ts 167 // The value assignment of the nested property cannot be observed. 168 this.title[0].value = 6; 169 ``` 170 171- When the decorated variable is of the Date type, the overall value assignment of the **Date** object can be observed, and the following APIs can be called to update **Date** properties: **setFullYear**, **setMonth**, **setDate**, **setHours**, **setMinutes**, **setSeconds**, **setMilliseconds**, **setTime**, **setUTCFullYear**, **setUTCMonth**, **setUTCDate**, **setUTCHours**, **setUTCMinutes**, **setUTCSeconds**, and **setUTCMilliseconds**. 172 173 ```ts 174 @Entry 175 @Component 176 struct DatePickerExample { 177 @State selectedDate: Date = new Date('2021-08-08'); 178 179 build() { 180 Column() { 181 Button('set selectedDate to 2023-07-08') 182 .margin(10) 183 .onClick(() => { 184 this.selectedDate = new Date('2023-07-08'); 185 }) 186 Button('increase the year by 1') 187 .margin(10) 188 .onClick(() => { 189 this.selectedDate.setFullYear(this.selectedDate.getFullYear() + 1); 190 }) 191 Button('increase the month by 1') 192 .margin(10) 193 .onClick(() => { 194 this.selectedDate.setMonth(this.selectedDate.getMonth() + 1); 195 }) 196 Button('increase the day by 1') 197 .margin(10) 198 .onClick(() => { 199 this.selectedDate.setDate(this.selectedDate.getDate() + 1); 200 }) 201 DatePicker({ 202 start: new Date('1970-1-1'), 203 end: new Date('2100-1-1'), 204 selected: this.selectedDate 205 }) 206 }.width('100%') 207 } 208 } 209 ``` 210 211- When the decorated variable is **Map**, value changes of **Map** can be observed. In addition, you can call the **set**, **clear**, and **delete** APIs of **Map** to update its value. For details, see [Decorating Variables of the Map Type](#decorating-variables-of-the-map-type). 212 213- When the decorated variable is **Set**, value changes of **Set** can be observed. In addition, you can call the **add**, **clear**, and **delete** APIs of **Set** to update its value. For details, see [Decorating Variables of the Set Type](#decorating-variables-of-the-set-type). 214 215### Framework Behavior 216 217- When a state variable is changed, the framework searches for components that depend on this state variable. 218 219- The framework executes an update method of the dependent components, which triggers re-rendering of the components. 220 221- Components or UI descriptions irrelevant to the state variable are not re-rendered, thereby implementing on-demand page updates. 222 223 224## Constraints 225 2261. Variables decorated by \@State must be initialized. Otherwise, an error is reported during compilation. 227 228 ```ts 229 // Incorrect format. An error is reported during compilation. 230 @State count: number; 231 232 // Correct format. 233 @State count: number = 10; 234 ``` 235 2362. \@State cannot decorate variables of the function type. Otherwise, the framework throws a runtime error. 237 238 239## Use Scenarios 240 241 242### Decorating Variables of Simple Types 243 244In this example, \@State is used to decorate the **count** variable of the simple type, turning it into a state variable. The change of **count** causes the update of the **Button** component. 245 246- When **count** changes, the framework searches for components bound to it, which include only the **Button** component in this example. 247 248- The framework executes the update method of the **Button** component to implement on-demand update. 249 250 251```ts 252@Entry 253@Component 254struct MyComponent { 255 @State count: number = 0; 256 257 build() { 258 Button(`click times: ${this.count}`) 259 .onClick(() => { 260 this.count += 1; 261 }) 262 } 263} 264``` 265 266 267### Decorating Variables of the Class Object Type 268 269- In this example, \@State is used to decorate the variables **count** and **title** in the custom component **MyComponent**. The type of **title** is **Model**, a custom class. If the value of **count** or **title** changes, the framework searches for all **MyComponent** instances that depend on these variables and triggers re-rendering of them. 270 271- The **EntryComponent** has multiple **MyComponent** instances. The internal state change of the first **MyComponent** instance does not affect the second **MyComponent** instance. 272 273```ts 274class Model { 275 public value: string; 276 277 constructor(value: string) { 278 this.value = value; 279 } 280} 281 282@Entry 283@Component 284struct EntryComponent { 285 build() { 286 Column() { 287 // The parameters specified here will overwrite the default values defined locally during initial render. Not all parameters need to be initialized from the parent component. 288 MyComponent({ count: 1, increaseBy: 2 }) 289 .width(300) 290 MyComponent({ title: new Model('Hello World 2'), count: 7 }) 291 } 292 } 293} 294 295@Component 296struct MyComponent { 297 @State title: Model = new Model('Hello World'); 298 @State count: number = 0; 299 private increaseBy: number = 1; 300 301 build() { 302 Column() { 303 Text(`${this.title.value}`) 304 .margin(10) 305 Button(`Click to change title`) 306 .onClick(() => { 307 // The update of the @State decorated variable triggers the update of the <Text> component. 308 this.title.value = this.title.value === 'Hello ArkUI' ? 'Hello World' : 'Hello ArkUI'; 309 }) 310 .width(300) 311 .margin(10) 312 313 Button(`Click to increase count = ${this.count}`) 314 .onClick(() => { 315 // The update of the @State decorated variable triggers the update of the <Button> component. 316 this.count += this.increaseBy; 317 }) 318 .width(300) 319 .margin(10) 320 } 321 } 322} 323``` 324 325 326 327In the preceding example, the initialization mechanism of the \@State variable is as follows: 328 329 3301. If no value is passed from the external, the default value is used for local initialization. 331 332 ```ts 333 // No external value is passed to title. Use the local value new Model('Hello World') for initialization. 334 MyComponent({ count: 1, increaseBy: 2 }) 335 // No external value is passed to increaseBy. Use the local value 1 for initialization. 336 MyComponent({ title: new Model('Hello World 2'), count: 7 }) 337 ``` 338 3392. If a value is passed from the external, use this value for initialization. 340 341 ```ts 342 // Used 1 and 2 passed from the external to initialize count and increaseBy. 343 MyComponent({ count: 1, increaseBy: 2 }) 344 // Used new Model('Hello World 2') and 7 passed from the external to initialize title and count. 345 MyComponent({ title: new Model('Hello World 2'), count: 7 }) 346 ``` 347 348 349### Decorating Variables of the Map Type 350 351> **NOTE** 352> 353> Since API version 11, \@State supports the Map type. 354 355In this example, the **message** variable is of the Map<number, string> type. When the button is clicked, the value of **message** changes, and the UI is re-rendered. 356 357```ts 358@Entry 359@Component 360struct MapSample { 361 @State message: Map<number, string> = new Map([[0, "a"], [1, "b"], [3, "c"]]); 362 363 build() { 364 Row() { 365 Column() { 366 ForEach(Array.from(this.message.entries()), (item: [number, string]) => { 367 Text(`${item[0]}`).fontSize(30) 368 Text(`${item[1]}`).fontSize(30) 369 Divider() 370 }) 371 Button('init map').onClick(() => { 372 this.message = new Map([[0, "a"], [1, "b"], [3, "c"]]); 373 }) 374 Button('set new one').onClick(() => { 375 this.message.set(4, "d"); 376 }) 377 Button('clear').onClick(() => { 378 this.message.clear(); 379 }) 380 Button('replace the first one').onClick(() => { 381 this.message.set(0, "aa"); 382 }) 383 Button('delete the first one').onClick(() => { 384 this.message.delete(0); 385 }) 386 } 387 .width('100%') 388 } 389 .height('100%') 390 } 391} 392``` 393 394### Decorating Variables of the Set Type 395 396> **NOTE** 397> 398> Since API version 11, \@State supports the Set type. 399 400In this example, the **message** variable is of the Set\<number\> type. When the button is clicked, the value of **message** changes, and the UI is re-rendered. 401 402```ts 403@Entry 404@Component 405struct SetSample { 406 @State message: Set<number> = new Set([0, 1, 2, 3, 4]); 407 408 build() { 409 Row() { 410 Column() { 411 ForEach(Array.from(this.message.entries()), (item: [number]) => { 412 Text(`${item[0]}`).fontSize(30) 413 Divider() 414 }) 415 Button('init set').onClick(() => { 416 this.message = new Set([0, 1, 2, 3, 4]); 417 }) 418 Button('set new one').onClick(() => { 419 this.message.add(5); 420 }) 421 Button('clear').onClick(() => { 422 this.message.clear(); 423 }) 424 Button('delete the first one').onClick(() => { 425 this.message.delete(0); 426 }) 427 } 428 .width('100%') 429 } 430 .height('100%') 431 } 432} 433``` 434 435## Union Type 436 437\@State supports **undefined**, **null**, and union types. In the following example, the type of **count** is **number | undefined**. If the property or type of **count** is changed when the button is clicked, the change will be synced to the view. 438 439```ts 440@Entry 441@Component 442struct EntryComponent { 443 build() { 444 Column() { 445 MyComponent() 446 } 447 } 448} 449 450@Component 451struct MyComponent { 452 @State count: number | undefined = 0; 453 454 build() { 455 Column() { 456 Text(`count(${this.count})`) 457 Button('change') 458 .onClick(() => { 459 this.count = undefined; 460 }) 461 } 462 } 463} 464``` 465 466 467## FAQs 468 469### Failure to Change a State Variable Using an Arrow Function 470 471The **this** object inside the arrow function's body is established based on the scope where the arrow function is defined points, not the scope where the arrow function is executed. As such, **this** of **changeCoverUrl** points to **PlayDetailViewModel** instead of the state variable decorated by \@State. 472 473Incorrect usage: 474 475```ts 476 477export default class PlayDetailViewModel { 478 coverUrl: string = '#00ff00'; 479 480 changeCoverUrl= ()=> { 481 this.coverUrl = '#00F5FF'; 482 } 483 484} 485``` 486 487```ts 488import PlayDetailViewModel from './PlayDetailViewModel'; 489 490@Entry 491@Component 492struct PlayDetailPage { 493 @State vm: PlayDetailViewModel = new PlayDetailViewModel(); 494 495 build() { 496 Stack() { 497 Text(this.vm.coverUrl).width(100).height(100).backgroundColor(this.vm.coverUrl) 498 Row() { 499 Button('Change Color') 500 .onClick(() => { 501 this.vm.changeCoverUrl(); 502 }) 503 } 504 } 505 .width('100%') 506 .height('100%') 507 .alignContent(Alignment.Top) 508 } 509} 510``` 511 512To fix the issue, pass **this.vm** and call the attribute of the decorated state variable to assign a value. 513 514Example: 515 516```ts 517 518export default class PlayDetailViewModel { 519 coverUrl: string = '#00ff00'; 520 521 changeCoverUrl= (model:PlayDetailViewModel)=> { 522 model.coverUrl = '#00F5FF'; 523 } 524 525} 526``` 527 528```ts 529import PlayDetailViewModel from './PlayDetailViewModel'; 530 531@Entry 532@Component 533struct PlayDetailPage { 534 @State vm: PlayDetailViewModel = new PlayDetailViewModel(); 535 536 build() { 537 Stack() { 538 Text(this.vm.coverUrl).width(100).height(100).backgroundColor(this.vm.coverUrl) 539 Row() { 540 Button('Change Color') 541 .onClick(() => { 542 let self = this.vm; 543 this.vm.changeCoverUrl(self); 544 }) 545 } 546 } 547 .width('100%') 548 .height('100%') 549 .alignContent(Alignment.Top) 550 } 551} 552``` 553 554### Capturing this in constructor() Fails to Observe Variable Changes 555 556In state management, classes are wrapped with a proxy. When a member variable of a class is changed in a component, the proxy intercepts the change. When the value in the data source is changed, the proxy notifies the bound component of the change. In this way, the change can be observed and trigger UI re-rendering. 557 558When an arrow function for changing **success** is initialized in the **constructor** function, the **TestModel** instance is not encapsulated by the proxy and is pointed by **this**. Therefore, the change of the query event triggered later cannot be observed by the state management. 559 560When the arrow function for changing **success** in **query**, the **TestModel** object has been initialized and encapsulated by the proxy. Call **this.viewModel.query()** and **this** in the function points to the **viewModel** object. In this case, the change of **isSuccess** is observable, so that the change of the query event can be observed by the state management. 561 562[Incorrect Usage] 563 564```ts 565@Entry 566@Component 567struct Index { 568 @State viewModel: TestModel = new TestModel(); 569 570 build() { 571 Row() { 572 Column() { 573 Text(this.viewModel.isSuccess ? 'success' : 'failed') 574 .fontSize(50) 575 .fontWeight(FontWeight.Bold) 576 .onClick(() => { 577 this.viewModel.query(); 578 }) 579 }.width('100%') 580 }.height('100%') 581 } 582} 583 584export class TestModel { 585 isSuccess: boolean = false; 586 model: Model 587 588 constructor() { 589 this.model = new Model(() => { 590 this.isSuccess = true; 591 console.log(`this.isSuccess: ${this.isSuccess}`); 592 }) 593 } 594 595 query() { 596 this.model.query(); 597 } 598} 599 600export class Model { 601 callback: () => void 602 603 constructor(cb: () => void) { 604 this.callback = cb; 605 } 606 607 query() { 608 this.callback(); 609 } 610} 611``` 612 613In the preceding example, the state variable is changed in the constructor. After the button is clicked, the change takes effect, indicated by "this.isSuccess: true" in the log. However, the page is not refreshed, and still displays "failed". 614 615[Example] 616 617```ts 618@Entry 619@Component 620struct Index { 621 @State viewModel: TestModel = new TestModel(); 622 623 build() { 624 Row() { 625 Column() { 626 Text(this.viewModel.isSuccess ? 'success' : 'failed') 627 .fontSize(50) 628 .fontWeight(FontWeight.Bold) 629 .onClick(() => { 630 this.viewModel.query(); 631 }) 632 }.width('100%') 633 }.height('100%') 634 } 635} 636 637export class TestModel { 638 isSuccess: boolean = false; 639 model: Model = new Model(() => { 640 }) 641 642 query() { 643 this.model.callback = () => { 644 this.isSuccess = true; 645 } 646 this.model.query(); 647 } 648} 649 650export class Model { 651 callback: () => void 652 653 constructor(cb: () => void) { 654 this.callback = cb; 655 } 656 657 query() { 658 this.callback(); 659 } 660} 661``` 662 663In the preceding example, the state variable is changed through a method of the class. After the button is clicked, the page content changes from "failed" to "success." 664 665### A state variable causes a re-render of merely the bound UI component. 666 667Example 1 668 669```ts 670class Info { 671 address: string = 'Hangzhou' 672} 673 674@Entry 675@Component 676struct Test { 677 @State message: string =' Shanghai' 678 @State info: Info = new Info(); 679 680 aboutToAppear(): void { 681 this.info.address = this.message; 682 } 683 684 build() { 685 Column() { 686 Text(`${this.message}`); 687 Text(`${this.info.address}`); 688 Button('change') 689 .onClick(() => { 690 this.info.address = 'Beijing' 691 }) 692 } 693 } 694} 695``` 696 697In the preceding example, when **Button('change')** is clicked, only the second **Text** component is re-rendered. Because **message** is of the simple type string, the value of this.message is not changed while the value of **address** in **info** is changed when the button is clicked. 698 699Example 2 700 701```ts 702class Info { 703 address: string = 'Hangzhou' 704 705 constructor(address: string) { 706 this.address = address; 707 } 708} 709 710class User { 711 info: Info = new Info('Tianjin'); 712} 713 714@Entry 715@Component 716struct Test { 717 @State info: Info = new Info('Shanghai'); 718 @State user: User = new User(); 719 720 aboutToAppear(): void { 721 this.user.info = this.info; 722 } 723 724 build() { 725 Column() { 726 Text(`${this.info.address}`); 727 Text(`${this.user.info.address}`); 728 Button('change') 729 .onClick(() => { 730 this.user.info.address = 'Beijing' 731 }) 732 } 733 } 734} 735``` 736 737In the preceding example, the reference of **info** is assigned to member property **info** of **user** in **aboutToAppear**. Therefore, when the button is clicked to change the property in **info**, the first **Text** component is re-rendered. However, only the first layer of the second **Text** component is observable, so the second **Text** component is not re-rendered. 738 739Example 3 740 741```ts 742class Info { 743 address: string = 'Hangzhou' 744 745 constructor(address: string) { 746 this.address = address; 747 } 748} 749 750class User { 751 info: Info = new Info('Tianjin'); 752} 753 754@Entry 755@Component 756struct Test { 757 @State info: Info = new Info('Shanghai'); 758 @State user: User = new User(); 759 760 aboutToAppear(): void { 761 this.user.info = this.info; 762 } 763 764 build() { 765 Column() { 766 Text(`${this.info.address}`); 767 Text(`${this.user.info.address}`); 768 Button('change') 769 .onClick(() => { 770 this.user.info = new Info('Guangzhou'); 771 this.user.info.address = 'Beijing' 772 }) 773 } 774 } 775} 776``` 777 778In the preceding example, if you click **Button('change')**, only the second **Text** component is re-rendered. This is because after the button is clicked, **this.user.info = new Info('Guangzhou')** creates a new **Info** object, and then **this.user.info.address = 'Beijing'** changes the value of **address** in the newly created **Info** object. However, the value of **address** in the original **Info** object is not changed. 779 780### Repeated Value Changes to State Variables by Complex Constants Trigger Re-rendering 781 782```ts 783class DataObj { 784 name: string = 'default name'; 785 786 constructor(name: string) { 787 this.name = name; 788 } 789} 790 791@Entry 792@Component 793struct Index { 794 list: DataObj[] = [new DataObj('a'), new DataObj('b'), new DataObj('c')]; 795 @State dataObjFromList: DataObj = this.list[0]; 796 797 build() { 798 Column() { 799 ConsumerChild({ dataObj: this.dataObjFromList }) 800 Button('change to self').onClick(() => { 801 this.dataObjFromList = this.list[0]; 802 }) 803 } 804 } 805} 806 807@Component 808struct ConsumerChild { 809 @Link @Watch('onDataObjChange') dataObj: DataObj; 810 811 onDataObjChange() { 812 console.log("dataObj changed"); 813 } 814 815 getContent() { 816 console.log(`this.dataObj.name change: ${this.dataObj.name}`); 817 return this.dataObj.name; 818 } 819 820 build() { 821 Column() { 822 Text(this.getContent()).fontSize(30) 823 } 824 } 825} 826``` 827 828In the preceding example, each time the **change to self** button is clicked, the same class constant is assigned to a state variable of the class type. This operation triggers re-render and generates the log **this.dataObj.name change: a**. In state management V1, a proxy is added to the @Observed decorated class objects and the @State decorated objects of the Class, Date, Map, Set, or Array type to observe the changes of top-level properties or API invoking. 829**dataObjFromList** is of a **Proxy** type but **list[0]** is of an **Object** type. As a result, when **list[0]** is assigned to **dataObjFromList**, the value changes trigger re-rendering. 830To avoid unnecessary value changes and re-renders, use \@Observed to decorate the class, or use [UIUtils.getTarget()](./arkts-new-getTarget.md) to obtain the original value and determine whether the original and new values are the same. If they are the same, do not perform value changes. 831Method 1: Add \@Observed decorator. 832 833```ts 834@Observed 835class DataObj { 836 name: string = 'default name'; 837 838 constructor(name: string) { 839 this.name = name; 840 } 841} 842 843@Entry 844@Component 845struct Index { 846 list: DataObj[] = [new DataObj('a'), new DataObj('b'), new DataObj('c')]; 847 @State dataObjFromList: DataObj = this.list[0]; 848 849 build() { 850 Column() { 851 ConsumerChild({ dataObj: this.dataObjFromList }) 852 Button('change to self').onClick(() => { 853 this.dataObjFromList = this.list[0]; 854 }) 855 } 856 } 857} 858 859@Component 860struct ConsumerChild { 861 @Link @Watch('onDataObjChange') dataObj: DataObj; 862 863 onDataObjChange() { 864 console.log("dataObj changed"); 865 } 866 867 build() { 868 Column() { 869 Text(this.dataObj.name).fontSize(30) 870 } 871 } 872} 873``` 874 875In the preceding example, the \@Observed decorator is added to decorate the class, **list[0]** is of the **Proxy** type. In this case, when a value is reassigned, the same object will not be re-rendered. 876 877Method 2: Use [UIUtils.getTarget()](./arkts-new-getTarget.md) to obtain the original object. 878 879```ts 880import { UIUtils } from '@ohos.arkui.StateManagement'; 881 882class DataObj { 883 name: string = 'default name'; 884 885 constructor(name: string) { 886 this.name = name; 887 } 888} 889 890@Entry 891@Component 892struct Index { 893 list: DataObj[] = [new DataObj('a'), new DataObj('b'), new DataObj('c')]; 894 @State dataObjFromList: DataObj = this.list[0]; 895 896 build() { 897 Column() { 898 ConsumerChild({ dataObj: this.dataObjFromList }) 899 Button('change to self').onClick(() => { 900 // Obtain the original value and compare it with the new value. 901 if (UIUtils.getTarget(this.dataObjFromList) !== this.list[0]) { 902 this.dataObjFromList = this.list[0]; 903 } 904 }) 905 } 906 } 907} 908 909@Component 910struct ConsumerChild { 911 @Link @Watch('onDataObjChange') dataObj: DataObj; 912 913 onDataObjChange() { 914 console.log("dataObj changed"); 915 } 916 917 build() { 918 Column() { 919 Text(this.dataObj.name).fontSize(30) 920 } 921 } 922} 923``` 924 925In the preceding example, **getTarget** is used to obtain the original value of the corresponding state variable before value change. After comparison, if the original value is the same as the new value, re-rendering will not be triggered. 926 927### Changing State Variables in build() Is Forbidden 928 929State variables cannot be changed in **build()**. Otherwise, the state management framework reports error logs during runtime. 930 931The rendering process is as follows: 932 9331. Create a custom component in **Index**. 934 9352. Execute the **build** method of **Index** as follows: 936 937 1. Create a **Column** component. 938 939 2. Create a Text component. **This.count++** is triggered when the **Text** component is created. 940 941 3. The value change of **count** triggers the re-render of the **Text** component. 942 943 4. Return value of **Text** is 2. 944 945```ts 946@Entry 947@Component 948struct Index { 949 @State count: number = 1; 950 951 build() { 952 Column() { 953 // Avoid directly changing the value of count in the Text component. 954 Text(`${this.count++}`) 955 .width(50) 956 .height(50) 957 } 958 } 959} 960``` 961 962During the first creation, the **Text** component is rendered twice. As a result, return value of the **Text** component is **2**. 963 964If the framework identifies that the state variable is changed in **build()**, an error log is generated. The error log is as follows: 965 966```ts 967FIX THIS APPLICATION ERROR: @Component 'Index'[4]: State variable 'count' has changed during render! It's illegal to change @Component state while build (initial render or re-render) is on-going. Application error! 968``` 969 970In the preceding example, this error does not cause serious consequences, for only the **Text** component is rendered one more time. Therefore, you may ignore this log. 971 972However, this behavior is a serious mistake. As the project becomes more complex, the potential risk becomes more and more serious.<br> Example: 973 974```ts 975@Entry 976@Component 977struct Index { 978 @State message: number = 20; 979 980 build() { 981 Column() { 982 Text(`${this.message++}`) 983 984 Text(`${this.message++}`) 985 } 986 .height('100%') 987 .width('100%') 988 } 989} 990``` 991The rendering process in the preceding example is as follows: 992 9931. Create the first **Text** component to trigger the change of **this.message**. 994 9952. The change of **this.message** triggers the re-render of the second **Text** component. 996 9973. The re-render of the second **Text** component triggers the change of **this.message**, which again triggers the re-render of the first **Text** component. 998 9994. Re-render is performed repeatedly. 1000 10015. The system does not respond for a long time, causing an App Freeze. 1002 1003Therefore, you are not advised to change the state variables in **build**. When the error "FIX THIS APPLICATION ERROR: @Component ... has changed during render! It's illegal to change @Component state while build (initial render or re-render) is on-going. Application error!" is reported, even if it does not bring serious consequences for now, you should pay attention to. Checking the application and modifying the corresponding error code to clear the error log are recommended. 1004 1005### Using the a.b(this.object) Format Fails to Trigger UI Re-render 1006 1007In the **build** method, when the variable decorated by \@State is of the object type and is called in the **a.b(this.object)** format, the original object of **this.object** is passed in the b method. If the property of **this.object** is changed, the UI cannot be re-rendered. In the following example, when the static method **Balloon.increaseVolume** or **this.reduceVolume** is used to change the **volume** of **Balloon**, the UI is not re-rendered. 1008 1009[Incorrect Usage] 1010 1011```ts 1012class Balloon { 1013 volume: number; 1014 constructor(volume: number) { 1015 this.volume = volume; 1016 } 1017 1018 static increaseVolume(balloon:Balloon) { 1019 balloon.volume += 2; 1020 } 1021} 1022 1023@Entry 1024@Component 1025struct Index { 1026 @State balloon: Balloon = new Balloon(10); 1027 1028 reduceVolume(balloon:Balloon) { 1029 balloon.volume -= 1; 1030 } 1031 1032 build() { 1033 Column({space:8}) { 1034 Text(`The volume of the balloon is ${this.balloon.volume} cubic centimeters.`) 1035 .fontSize(30) 1036 Button(`increaseVolume`) 1037 .onClick(()=>{ 1038 // The UI cannot be re-rendered using a static method. 1039 Balloon.increaseVolume(this.balloon); 1040 }) 1041 Button(`reduceVolume`) 1042 .onClick(()=>{ 1043 // The UI cannot be re-rendered using this. 1044 this.reduceVolume(this.balloon); 1045 }) 1046 } 1047 .width('100%') 1048 .height('100%') 1049 } 1050} 1051``` 1052 1053You can add a proxy for **this.balloon** to re-render the UI by assigning a value to the variable and then calling the variable. 1054 1055[Example] 1056 1057```ts 1058class Balloon { 1059 volume: number; 1060 constructor(volume: number) { 1061 this.volume = volume; 1062 } 1063 1064 static increaseVolume(balloon:Balloon) { 1065 balloon.volume += 2; 1066 } 1067} 1068 1069@Entry 1070@Component 1071struct Index { 1072 @State balloon: Balloon = new Balloon(10); 1073 1074 reduceVolume(balloon:Balloon) { 1075 balloon.volume -= 1; 1076 } 1077 1078 build() { 1079 Column({space:8}) { 1080 Text(`The volume of the balloon is ${this.balloon.volume} cubic centimeters.`) 1081 .fontSize(30) 1082 Button(`increaseVolume`) 1083 .onClick(()=>{ 1084 // Add a proxy by assigning a value. 1085 let balloon1 = this.balloon; 1086 Balloon.increaseVolume(balloon1); 1087 }) 1088 Button(`reduceVolume`) 1089 .onClick(()=>{ 1090 // Add a proxy by assigning a value. 1091 let balloon2 = this.balloon; 1092 this.reduceVolume(balloon2); 1093 }) 1094 } 1095 .width('100%') 1096 .height('100%') 1097 } 1098} 1099``` 1100 1101### Unregistration Existing Functions Before Changing State Variables by Registering a Callback 1102 1103You can register the arrow function in **aboutToAppear** to change the state variables in the component. Note that the registered function must be left empty in **aboutToDisappear**. Otherwise, the custom component cannot be released because the arrow function captures the **this** instance of the component, causing memory leakage. 1104 1105```ts 1106class Model { 1107 private callback: (() => void) | undefined = () => {}; 1108 1109 add(callback: () => void): void { 1110 this.callback = callback; 1111 } 1112 1113 delete(): void { 1114 this.callback = undefined; 1115 } 1116 1117 call(): void { 1118 if (this.callback) { 1119 this.callback(); 1120 } 1121 } 1122} 1123 1124let model: Model = new Model(); 1125 1126@Entry 1127@Component 1128struct Test { 1129 @State count: number = 10; 1130 1131 aboutToAppear(): void { 1132 model.add(() => { 1133 this.count++; 1134 }) 1135 } 1136 1137 build() { 1138 Column() { 1139 Text(`Value of count: ${this.count}`) 1140 Button('change') 1141 .onClick(() => { 1142 model.call(); 1143 }) 1144 } 1145 } 1146 1147 aboutToDisappear(): void { 1148 model.delete(); 1149 } 1150} 1151``` 1152 1153In addition, you can use [LocalStorage](./arkts-localstorage.md#changing-state-variables-outside-a-custom-component) to change the state variables outside a custom component. 1154