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 recommended; **@State a: string = undefined** is not recommended.| 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## Application 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 build() { 816 Column() { 817 Text(this.dataObj.name).fontSize(30) 818 } 819 } 820} 821``` 822 823In the preceding example, each time you click **Button('change to self')**, the same class constant is assigned to a state variable of the **Class** type, triggering re-rendering. 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. 824**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. 825To 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. 826Method 1: Add \@Observed decorator. 827 828```ts 829@Observed 830class DataObj { 831 name: string = 'default name'; 832 833 constructor(name: string) { 834 this.name = name; 835 } 836} 837 838@Entry 839@Component 840struct Index { 841 list: DataObj[] = [new DataObj('a'), new DataObj('b'), new DataObj('c')]; 842 @State dataObjFromList: DataObj = this.list[0]; 843 844 build() { 845 Column() { 846 ConsumerChild({ dataObj: this.dataObjFromList }) 847 Button('change to self').onClick(() => { 848 this.dataObjFromList = this.list[0]; 849 }) 850 } 851 } 852} 853 854@Component 855struct ConsumerChild { 856 @Link @Watch('onDataObjChange') dataObj: DataObj; 857 858 onDataObjChange() { 859 console.log("dataObj changed"); 860 } 861 862 build() { 863 Column() { 864 Text(this.dataObj.name).fontSize(30) 865 } 866 } 867} 868``` 869 870In 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. 871 872Method 2: Use [UIUtils.getTarget()](./arkts-new-getTarget.md) to obtain the original object. 873 874```ts 875import { UIUtils } from '@ohos.arkui.StateManagement'; 876 877class DataObj { 878 name: string = 'default name'; 879 880 constructor(name: string) { 881 this.name = name; 882 } 883} 884 885@Entry 886@Component 887struct Index { 888 list: DataObj[] = [new DataObj('a'), new DataObj('b'), new DataObj('c')]; 889 @State dataObjFromList: DataObj = this.list[0]; 890 891 build() { 892 Column() { 893 ConsumerChild({ dataObj: this.dataObjFromList }) 894 Button('change to self').onClick(() => { 895 // Obtain the original value and compare it with the new value. 896 if (UIUtils.getTarget(this.dataObjFromList) !== this.list[0]) { 897 this.dataObjFromList = this.list[0]; 898 } 899 }) 900 } 901 } 902} 903 904@Component 905struct ConsumerChild { 906 @Link @Watch('onDataObjChange') dataObj: DataObj; 907 908 onDataObjChange() { 909 console.log("dataObj changed"); 910 } 911 912 build() { 913 Column() { 914 Text(this.dataObj.name).fontSize(30) 915 } 916 } 917} 918``` 919 920In 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. 921 922### Changing State Variables in build() Is Forbidden 923 924State variables cannot be changed in **build()**. Otherwise, the state management framework reports error logs during runtime. 925 926The rendering process is as follows: 927 9281. Create a custom component in **Index**. 929 9302. Execute the **build** method of **Index** as follows: 931 932 1. Create a **Column** component. 933 934 2. Create a Text component. **This.count++** is triggered when the **Text** component is created. 935 936 3. The value change of **count** triggers the re-render of the **Text** component. 937 938 4. Return value of **Text** is 2. 939 940```ts 941@Entry 942@Component 943struct Index { 944 @State count: number = 1; 945 946 build() { 947 Column() { 948 // Avoid directly changing the value of count in the Text component. 949 Text(`${this.count++}`) 950 .width(50) 951 .height(50) 952 } 953 } 954} 955``` 956 957During the first creation, the **Text** component is rendered twice. As a result, return value of the **Text** component is **2**. 958 959If the framework identifies that the state variable is changed in **build()**, an error log is generated. The error log is as follows: 960 961```ts 962FIX 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! 963``` 964 965In 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. 966 967However, this behavior is a serious mistake. As the project becomes more complex, the potential risk becomes more and more serious.<br> Example: 968 969```ts 970@Entry 971@Component 972struct Index { 973 @State message: number = 20; 974 975 build() { 976 Column() { 977 Text(`${this.message++}`) 978 979 Text(`${this.message++}`) 980 } 981 .height('100%') 982 .width('100%') 983 } 984} 985``` 986The rendering process in the preceding example is as follows: 987 9881. Create the first **Text** component to trigger the change of **this.message**. 989 9902. The change of **this.message** triggers the re-render of the second **Text** component. 991 9923. 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. 993 9944. Re-render is performed repeatedly. 995 9965. The system does not respond for a long time, causing an App Freeze. 997 998Therefore, 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. 999 1000### Using the a.b(this.object) Format Fails to Trigger UI Re-render 1001 1002In 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 native 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. 1003 1004[Incorrect Usage] 1005 1006```ts 1007class Balloon { 1008 volume: number; 1009 constructor(volume: number) { 1010 this.volume = volume; 1011 } 1012 1013 static increaseVolume(balloon:Balloon) { 1014 balloon.volume += 2; 1015 } 1016} 1017 1018@Entry 1019@Component 1020struct Index { 1021 @State balloon: Balloon = new Balloon(10); 1022 1023 reduceVolume(balloon:Balloon) { 1024 balloon.volume -= 1; 1025 } 1026 1027 build() { 1028 Column({space:8}) { 1029 Text(`The volume of the balloon is ${this.balloon.volume} cubic centimeters.`) 1030 .fontSize(30) 1031 Button(`increaseVolume`) 1032 .onClick(()=>{ 1033 // The UI cannot be re-rendered using a static method. 1034 Balloon.increaseVolume(this.balloon); 1035 }) 1036 Button(`reduceVolume`) 1037 .onClick(()=>{ 1038 // The UI cannot be re-rendered using this. 1039 this.reduceVolume(this.balloon); 1040 }) 1041 } 1042 .width('100%') 1043 .height('100%') 1044 } 1045} 1046``` 1047 1048You can add a proxy for **this.balloon** to re-render the UI by assigning a value to the variable and then calling the variable. 1049 1050[Example] 1051 1052```ts 1053class Balloon { 1054 volume: number; 1055 constructor(volume: number) { 1056 this.volume = volume; 1057 } 1058 1059 static increaseVolume(balloon:Balloon) { 1060 balloon.volume += 2; 1061 } 1062} 1063 1064@Entry 1065@Component 1066struct Index { 1067 @State balloon: Balloon = new Balloon(10); 1068 1069 reduceVolume(balloon:Balloon) { 1070 balloon.volume -= 1; 1071 } 1072 1073 build() { 1074 Column({space:8}) { 1075 Text(`The volume of the balloon is ${this.balloon.volume} cubic centimeters.`) 1076 .fontSize(30) 1077 Button(`increaseVolume`) 1078 .onClick(()=>{ 1079 // Add a proxy by assigning a value. 1080 let balloon1 = this.balloon; 1081 Balloon.increaseVolume(balloon1); 1082 }) 1083 Button(`reduceVolume`) 1084 .onClick(()=>{ 1085 // Add a proxy by assigning a value. 1086 let balloon2 = this.balloon; 1087 this.reduceVolume(balloon2); 1088 }) 1089 } 1090 .width('100%') 1091 .height('100%') 1092 } 1093} 1094``` 1095 1096### Changing State Variables Outside a Custom Component 1097 1098You 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. 1099 1100```ts 1101class Model { 1102 private callback: Function | undefined = () => {} 1103 1104 add(callback: () => void): void { 1105 this.callback = callback; 1106 } 1107 1108 delete(): void { 1109 this.callback = undefined; 1110 } 1111 1112 call(): void { 1113 if (this.callback) { 1114 this.callback(); 1115 } 1116 } 1117} 1118 1119let model: Model = new Model(); 1120 1121@Entry 1122@Component 1123struct Test { 1124 @State count: number = 10; 1125 1126 aboutToAppear(): void { 1127 model.add(() => { 1128 this.count++; 1129 }) 1130 } 1131 1132 build() { 1133 Column() { 1134 Text(`Value of count: ${this.count}`) 1135 Button('change') 1136 .onClick(() => { 1137 model.call(); 1138 }) 1139 } 1140 } 1141 1142 aboutToDisappear(): void { 1143 model.delete(); 1144 } 1145} 1146``` 1147 1148In addition, you can use [LocalStorage](./arkts-localstorage.md#changing-state-variables-outside-a-custom-component) to change the state variables outside a custom component. 1149