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 Declare the **Person** and **Model** classes. 69 70 ```ts 71 class Person { 72 public value: string; 73 74 constructor(value: string) { 75 this.value = value; 76 } 77 } 78 79 class Model { 80 public value: string; 81 public name: Person; 82 constructor(value: string, person: Person) { 83 this.value = value; 84 this.name = person; 85 } 86 } 87 ``` 88 89 Use \@State to decorate a variable of the Model class object type. 90 91 ```ts 92 // Class type 93 @State title: Model = new Model('Hello', new Person('World')); 94 ``` 95 96 Assign a value to the \@State decorated variable. 97 98 ```ts 99 // Assign a value to the class object. 100 this.title = new Model('Hi', new Person('ArkUI')); 101 ``` 102 103 Assign a value to a property of the \@State decorated variable. 104 105 ```ts 106 // Assign a value to a property of the class object. 107 this.title.value = 'Hi'; 108 ``` 109 110 The value assignment of the nested property cannot be observed. 111 112 ```ts 113 // The value assignment of the nested property cannot be observed. 114 this.title.name.value = 'ArkUI'; 115 ``` 116- When the decorated variable is of the array type, the addition, deletion, and updates of array items can be observed. Below are some examples: 117 Declare the **Model** class. 118 119 ```ts 120 class Model { 121 public value: number; 122 constructor(value: number) { 123 this.value = value; 124 } 125 } 126 ``` 127 128 Use \@State to decorate a variable of the Model class array type. 129 130 ```ts 131 // Array type 132 @State title: Model[] = [new Model(11), new Model(1)]; 133 ``` 134 135 The value assignment of the array itself can be observed. 136 137 ```ts 138 // Value assignment of the array 139 this.title = [new Model(2)]; 140 ``` 141 142 The value assignment of array items can be observed. 143 144 ```ts 145 // Value assignment of an array item 146 this.title[0] = new Model(2); 147 ``` 148 149 The deletion of array items can be observed. 150 151 ```ts 152 // Array item change 153 this.title.pop(); 154 ``` 155 156 The addition of array items can be observed. 157 158 ```ts 159 // Array item change 160 this.title.push(new Model(12)); 161 ``` 162 163 The property value assignment in the array items cannot be observed. 164 165 ```ts 166 // The value assignment of the nested property cannot be observed. 167 this.title[0].value = 6; 168 ``` 169 170- 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**. 171 172 ```ts 173 @Entry 174 @Component 175 struct DatePickerExample { 176 @State selectedDate: Date = new Date('2021-08-08'); 177 178 build() { 179 Column() { 180 Button('set selectedDate to 2023-07-08') 181 .margin(10) 182 .onClick(() => { 183 this.selectedDate = new Date('2023-07-08'); 184 }) 185 Button('increase the year by 1') 186 .margin(10) 187 .onClick(() => { 188 this.selectedDate.setFullYear(this.selectedDate.getFullYear() + 1); 189 }) 190 Button('increase the month by 1') 191 .margin(10) 192 .onClick(() => { 193 this.selectedDate.setMonth(this.selectedDate.getMonth() + 1); 194 }) 195 Button('increase the day by 1') 196 .margin(10) 197 .onClick(() => { 198 this.selectedDate.setDate(this.selectedDate.getDate() + 1); 199 }) 200 DatePicker({ 201 start: new Date('1970-1-1'), 202 end: new Date('2100-1-1'), 203 selected: this.selectedDate 204 }) 205 }.width('100%') 206 } 207 } 208 ``` 209 210- 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). 211 212- 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). 213 214### Framework Behavior 215 216- When a state variable is changed, the framework searches for components that depend on this state variable. 217 218- The framework executes an update method of the dependent components, which triggers re-rendering of the components. 219 220- Components or UI descriptions irrelevant to the state variable are not re-rendered, thereby implementing on-demand page updates. 221 222 223## Constraints 224 2251. Variables decorated by \@State must be initialized. Otherwise, an error is reported during compilation. 226 227```ts 228// Incorrect format. An error is reported during compilation. 229@State count: number; 230 231// Correct format. 232@State count: number = 10; 233``` 234 2352. \@State cannot decorate variables of the function type. Otherwise, the framework throws a runtime error. 236 237 238## Application Scenarios 239 240 241### Decorating Variables of Simple Types 242 243In 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. 244 245- When **count** changes, the framework searches for components bound to it, which include only the **Button** component in this example. 246 247- The framework executes the update method of the **Button** component to implement on-demand update. 248 249 250```ts 251@Entry 252@Component 253struct MyComponent { 254 @State count: number = 0; 255 256 build() { 257 Button(`click times: ${this.count}`) 258 .onClick(() => { 259 this.count += 1; 260 }) 261 } 262} 263``` 264 265 266### Decorating Variables of the Class Object Type 267 268- 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. 269 270- The **EntryComponent** has multiple **MyComponent** instances. The internal state change of the first **MyComponent** instance does not affect the second **MyComponent** instance. 271 272 273 274```ts 275class Model { 276 public value: string; 277 278 constructor(value: string) { 279 this.value = value; 280 } 281} 282 283@Entry 284@Component 285struct EntryComponent { 286 build() { 287 Column() { 288 // 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. 289 MyComponent({ count: 1, increaseBy: 2 }) 290 .width(300) 291 MyComponent({ title: new Model('Hello World 2'), count: 7 }) 292 } 293 } 294} 295 296@Component 297struct MyComponent { 298 @State title: Model = new Model('Hello World'); 299 @State count: number = 0; 300 private increaseBy: number = 1; 301 302 build() { 303 Column() { 304 Text(`${this.title.value}`) 305 .margin(10) 306 Button(`Click to change title`) 307 .onClick(() => { 308 // The update of the @State decorated variable triggers the update of the <Text> component. 309 this.title.value = this.title.value === 'Hello ArkUI' ? 'Hello World' : 'Hello ArkUI'; 310 }) 311 .width(300) 312 .margin(10) 313 314 Button(`Click to increase count = ${this.count}`) 315 .onClick(() => { 316 // The update of the @State decorated variable triggers the update of the <Button> component. 317 this.count += this.increaseBy; 318 }) 319 .width(300) 320 .margin(10) 321 } 322 } 323} 324``` 325 326 327 328In the preceding example, the initialization mechanism of the \@State variable is as follows: 329 330 3311. If no value is passed from the external, the default value is used for local initialization. 332 333 ```ts 334 // No external value is passed to title. Use the local value new Model('Hello World') for initialization. 335 MyComponent({ count: 1, increaseBy: 2 }) 336 // No external value is passed to increaseBy. Use the local value 1 for initialization. 337 MyComponent({ title: new Model('Hello World 2'), count: 7 }) 338 ``` 339 3402. If a value is passed from the external, use this value for initialization. 341 342 ```ts 343 // Used 1 and 2 passed from the external to initialize count and increaseBy. 344 MyComponent({ count: 1, increaseBy: 2 }) 345 // Used new Model('Hello World 2') and 7 passed from the external to initialize title and count. 346 MyComponent({ title: new Model('Hello World 2'), count: 7 }) 347 ``` 348 349 350### Decorating Variables of the Map Type 351 352> **NOTE** 353> 354> Since API version 11, \@State supports the Map type. 355 356In 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. 357 358```ts 359@Entry 360@Component 361struct MapSample { 362 @State message: Map<number, string> = new Map([[0, "a"], [1, "b"], [3, "c"]]); 363 364 build() { 365 Row() { 366 Column() { 367 ForEach(Array.from(this.message.entries()), (item: [number, string]) => { 368 Text(`${item[0]}`).fontSize(30) 369 Text(`${item[1]}`).fontSize(30) 370 Divider() 371 }) 372 Button('init map').onClick(() => { 373 this.message = new Map([[0, "a"], [1, "b"], [3, "c"]]); 374 }) 375 Button('set new one').onClick(() => { 376 this.message.set(4, "d"); 377 }) 378 Button('clear').onClick(() => { 379 this.message.clear(); 380 }) 381 Button('replace the first one').onClick(() => { 382 this.message.set(0, "aa"); 383 }) 384 Button('delete the first one').onClick(() => { 385 this.message.delete(0); 386 }) 387 } 388 .width('100%') 389 } 390 .height('100%') 391 } 392} 393``` 394 395### Decorating Variables of the Set Type 396 397> **NOTE** 398> 399> Since API version 11, \@State supports the Set type. 400 401In 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. 402 403```ts 404@Entry 405@Component 406struct SetSample { 407 @State message: Set<number> = new Set([0, 1, 2, 3, 4]); 408 409 build() { 410 Row() { 411 Column() { 412 ForEach(Array.from(this.message.entries()), (item: [number]) => { 413 Text(`${item[0]}`).fontSize(30) 414 Divider() 415 }) 416 Button('init set').onClick(() => { 417 this.message = new Set([0, 1, 2, 3, 4]); 418 }) 419 Button('set new one').onClick(() => { 420 this.message.add(5); 421 }) 422 Button('clear').onClick(() => { 423 this.message.clear(); 424 }) 425 Button('delete the first one').onClick(() => { 426 this.message.delete(0); 427 }) 428 } 429 .width('100%') 430 } 431 .height('100%') 432 } 433} 434``` 435 436## Union Type 437 438\@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. 439 440```ts 441@Entry 442@Component 443struct EntryComponent { 444 build() { 445 Column() { 446 MyComponent() 447 } 448 } 449} 450 451@Component 452struct MyComponent { 453 @State count: number | undefined = 0; 454 455 build() { 456 Column() { 457 Text(`count(${this.count})`) 458 Button('change') 459 .onClick(() => { 460 this.count = undefined; 461 }) 462 } 463 } 464} 465``` 466 467 468## FAQs 469 470### Failure to Change a State Variable Using an Arrow Function 471 472The **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. 473 474Incorrect usage: 475 476```ts 477 478export default class PlayDetailViewModel { 479 coverUrl: string = '#00ff00' 480 481 changeCoverUrl= ()=> { 482 this.coverUrl = '#00F5FF'; 483 } 484 485} 486``` 487 488```ts 489import PlayDetailViewModel from './PlayDetailViewModel' 490 491@Entry 492@Component 493struct PlayDetailPage { 494 @State vm: PlayDetailViewModel = new PlayDetailViewModel(); 495 496 build() { 497 Stack() { 498 Text(this.vm.coverUrl).width(100).height(100).backgroundColor(this.vm.coverUrl) 499 Row() { 500 Button('Change Color') 501 .onClick(() => { 502 this.vm.changeCoverUrl(); 503 }) 504 } 505 } 506 .width('100%') 507 .height('100%') 508 .alignContent(Alignment.Top) 509 } 510} 511``` 512 513To fix the issue, pass **this.vm** and call the attribute of the decorated state variable to assign a value. 514 515Example: 516 517```ts 518 519export default class PlayDetailViewModel { 520 coverUrl: string = '#00ff00' 521 522 changeCoverUrl= (model:PlayDetailViewModel)=> { 523 model.coverUrl = '#00F5FF' 524 } 525 526} 527``` 528 529```ts 530import PlayDetailViewModel from './PlayDetailViewModel' 531 532@Entry 533@Component 534struct PlayDetailPage { 535 @State vm: PlayDetailViewModel = new PlayDetailViewModel(); 536 537 build() { 538 Stack() { 539 Text(this.vm.coverUrl).width(100).height(100).backgroundColor(this.vm.coverUrl) 540 Row() { 541 Button('Change Color') 542 .onClick(() => { 543 let self = this.vm; 544 this.vm.changeCoverUrl(self); 545 }) 546 } 547 } 548 .width('100%') 549 .height('100%') 550 .alignContent(Alignment.Top) 551 } 552} 553``` 554 555### State Variable Changes in the Constructor Not Taking Effect 556 557In 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. 558 559If you initialize the arrow function for modifying **success** in the constructor, **this** points to the original **TestModel** class, which has not been wrapped with a proxy. As a result, the change can be observed through query event triggering. 560 561To enable the change to be observable, place the arrow function for modifying **success** in **query**. As **query** is wrapped with a proxy and has an object initialized, **this** points to the proxy object. 562 563[Incorrect Usage] 564 565```ts 566@Entry 567@Component 568struct Index { 569 @State viewModel: TestModel = new TestModel(); 570 571 build() { 572 Row() { 573 Column() { 574 Text(this.viewModel.isSuccess ? 'success' : 'failed') 575 .fontSize(50) 576 .fontWeight(FontWeight.Bold) 577 .onClick(() => { 578 this.viewModel.query(); 579 }) 580 }.width('100%') 581 }.height('100%') 582 } 583} 584 585export class TestModel { 586 isSuccess: boolean = false; 587 model: Model 588 589 constructor() { 590 this.model = new Model(() => { 591 this.isSuccess = true; 592 console.log(`this.isSuccess: ${this.isSuccess}`); 593 }) 594 } 595 596 query() { 597 this.model.query(); 598 } 599} 600 601export class Model { 602 callback: () => void 603 604 constructor(cb: () => void) { 605 this.callback = cb; 606 } 607 608 query() { 609 this.callback(); 610 } 611} 612``` 613 614In 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". 615 616[Example] 617 618```ts 619@Entry 620@Component 621struct Index { 622 @State viewModel: TestModel = new TestModel(); 623 624 build() { 625 Row() { 626 Column() { 627 Text(this.viewModel.isSuccess ? 'success' : 'failed') 628 .fontSize(50) 629 .fontWeight(FontWeight.Bold) 630 .onClick(() => { 631 this.viewModel.query(); 632 }) 633 }.width('100%') 634 }.height('100%') 635 } 636} 637 638export class TestModel { 639 isSuccess: boolean = false; 640 model: Model = new Model(() => { 641 }) 642 643 query() { 644 this.model.callback = () => { 645 this.isSuccess = true; 646 } 647 this.model.query(); 648 } 649} 650 651export class Model { 652 callback: () => void 653 654 constructor(cb: () => void) { 655 this.callback = cb; 656 } 657 658 query() { 659 this.callback(); 660 } 661} 662``` 663 664In 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." 665 666### A state variable causes a re-render of merely the bound UI component. 667 668Example 1 669 670```ts 671class Parent { 672 son: string = '000'; 673} 674 675@Entry 676@Component 677struct Test { 678 @State son: string = '111'; 679 @State parent: Parent = new Parent(); 680 681 aboutToAppear(): void { 682 this.parent.son = this.son; 683 } 684 685 build() { 686 Column() { 687 Text(`${this.son}`); 688 Text(`${this.parent.son}`); 689 Button('change') 690 .onClick(() => { 691 this.parent.son = '222'; 692 }) 693 } 694 } 695} 696``` 697 698In the preceding example, clicking **Button('change')** changes the second-line text from **'111'** to **'222'**, but does not change the first-line text. This is because **son** is of the primitive string type, for which a shallow copy is performed. In shallow copy, clicking the button changes the value of **son** in **parent**, but the value of **this.son** remains unchanged. 699 700Example 2 701 702```ts 703class Son { 704 son: string = '000'; 705 706 constructor(son: string) { 707 this.son = son; 708 } 709} 710 711class Parent { 712 son: Son = new Son('111'); 713} 714 715@Entry 716@Component 717struct Test { 718 @State son: Son = new Son('222'); 719 @State parent: Parent = new Parent(); 720 721 aboutToAppear(): void { 722 this.parent.son = this.son; 723 } 724 725 build() { 726 Column() { 727 Text(`${this.son.son}`); 728 Text(`${this.parent.son.son}`); 729 Button('change') 730 .onClick(() => { 731 this.parent.son.son = '333'; 732 }) 733 } 734 } 735} 736``` 737 738In the preceding example, a reference of **son** is assigned to the **son** property of **parent** in **aboutToAppear**. In this case, if you click the button to change the property in **son**, the first **Text** component is re-rendered, but not the second **Text** component, which is unable to observe changes at the second layer. 739 740Example 3 741 742```ts 743class Son { 744 son: string = '000'; 745 746 constructor(son: string) { 747 this.son = son; 748 } 749} 750 751class Parent { 752 son: Son = new Son('111'); 753} 754 755@Entry 756@Component 757struct Test { 758 @State son: Son = new Son('222'); 759 @State parent: Parent = new Parent(); 760 761 aboutToAppear(): void { 762 this.parent.son = this.son; 763 } 764 765 build() { 766 Column() { 767 Text(`${this.son.son}`); 768 Text(`${this.parent.son.son}`); 769 Button('change') 770 .onClick(() => { 771 this.parent.son = new Son('444'); 772 this.parent.son.son = '333'; 773 }) 774 } 775 } 776} 777``` 778 779In the preceding example, clicking **Button('change')** changes the second-line text from **'222'** to **'333'**, but does not change the first-line text. This is because **this.parent.son = new Son('444')'** is executed after the button is clicked, which means that a **Son** object is created, and then **this.parent.son.son = '333'** is executed. As a result, clicking the button changes the value of **son** in the new **Son** object, and the one in the original Son object is not affected. 780 781### Repeated Value Changes to State Variables by Complex Constants Trigger Re-rendering 782 783```ts 784class DataObj { 785 name: string = 'default name'; 786 787 constructor(name: string) { 788 this.name = name; 789 } 790} 791 792@Entry 793@Component 794struct Index { 795 list: DataObj[] = [new DataObj('a'), new DataObj('b'), new DataObj('c')]; 796 @State dataObjFromList: DataObj = this.list[0]; 797 798 build() { 799 Column() { 800 ConsumerChild({ dataObj: this.dataObjFromList }) 801 Button('change to self').onClick(() => { 802 this.dataObjFromList = this.list[0]; 803 }) 804 } 805 } 806} 807 808@Component 809struct ConsumerChild { 810 @Link @Watch('onDataObjChange') dataObj: DataObj; 811 812 onDataObjChange() { 813 console.log("dataObj changed"); 814 } 815 816 build() { 817 Column() { 818 Text(this.dataObj.name).fontSize(30) 819 } 820 } 821} 822``` 823 824In 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. 825**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. 826To 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. 827Method 1: Add \@Observed decorator. 828 829```ts 830@Observed 831class DataObj { 832 name: string = 'default name'; 833 834 constructor(name: string) { 835 this.name = name; 836 } 837} 838 839@Entry 840@Component 841struct Index { 842 list: DataObj[] = [new DataObj('a'), new DataObj('b'), new DataObj('c')]; 843 @State dataObjFromList: DataObj = this.list[0]; 844 845 build() { 846 Column() { 847 ConsumerChild({ dataObj: this.dataObjFromList }) 848 Button('change to self').onClick(() => { 849 this.dataObjFromList = this.list[0]; 850 }) 851 } 852 } 853} 854 855@Component 856struct ConsumerChild { 857 @Link @Watch('onDataObjChange') dataObj: DataObj; 858 859 onDataObjChange() { 860 console.log("dataObj changed"); 861 } 862 863 build() { 864 Column() { 865 Text(this.dataObj.name).fontSize(30) 866 } 867 } 868} 869``` 870 871In 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. 872 873Method 2: Use [UIUtils.getTarget()](./arkts-new-getTarget.md) to obtain the original object. 874 875```ts 876import { UIUtils } from '@ohos.arkui.StateManagement'; 877 878class DataObj { 879 name: string = 'default name'; 880 881 constructor(name: string) { 882 this.name = name; 883 } 884} 885 886@Entry 887@Component 888struct Index { 889 list: DataObj[] = [new DataObj('a'), new DataObj('b'), new DataObj('c')]; 890 @State dataObjFromList: DataObj = this.list[0]; 891 892 build() { 893 Column() { 894 ConsumerChild({ dataObj: this.dataObjFromList }) 895 Button('change to self').onClick(() => { 896 // Obtain the original value and compare it with the new value. 897 if (UIUtils.getTarget(this.dataObjFromList) !== this.list[0]) { 898 this.dataObjFromList = this.list[0]; 899 } 900 }) 901 } 902 } 903} 904 905@Component 906struct ConsumerChild { 907 @Link @Watch('onDataObjChange') dataObj: DataObj; 908 909 onDataObjChange() { 910 console.log("dataObj changed"); 911 } 912 913 build() { 914 Column() { 915 Text(this.dataObj.name).fontSize(30) 916 } 917 } 918} 919``` 920 921In 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. 922 923### State Variables Modification in build() Is Forbidden 924 925State variables cannot be changed in **build()**. Otherwise, the state management framework reports error logs during runtime. 926 927The rendering process is as follows: 928 9291. Create a custom component in **Index**. 930 9312. Execute the **build** method of **Index** as follows: 932 933 1. Create a **Column** component. 934 935 2. Create a Text component. **This.count++** is triggered when the **Text** component is created. 936 937 3. The value change of **count** triggers the re-render of the **Text** component. 938 939 4. Return value of **Text** is 2. 940 941```ts 942@Entry 943@Component 944struct Index { 945 @State count: number = 1; 946 947 build() { 948 Column() { 949 // Avoid directly changing the value of count in the Text component. 950 Text(`${this.count++}`) 951 .width(50) 952 .height(50) 953 } 954 } 955} 956``` 957 958During the first creation, the **Text** component is rendered twice. As a result, return value of the **Text** component is **2**. 959 960If the framework identifies that the state variable is changed in **build()**, an error log is generated. The error log is as follows: 961 962```ts 963FIX 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! 964``` 965 966In 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. 967 968However, this behavior is a serious mistake. As the project becomes more complex, the potential risk becomes more and more serious.<br> Example: 969 970```ts 971@Entry 972@Component 973struct Index { 974 @State message: number = 20; 975 976 build() { 977 Column() { 978 Text(`${this.message++}`) 979 980 Text(`${this.message++}`) 981 } 982 .height('100%') 983 .width('100%') 984 } 985} 986``` 987The rendering process in the preceding example is as follows: 988 9891. Create the first **Text** component to trigger the change of **this.message**. 990 9912. The change of **this.message** triggers the re-render of the second **Text** component. 992 9933. 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. 994 9954. Re-render is performed repeatedly. 996 9975. The system does not respond for a long time, causing an App Freeze. 998 999Therefore, 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. 1000 1001### Using the a.b(this.object) Format Fails to Trigger UI Re-render 1002 1003In 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. 1004 1005[Incorrect Usage] 1006 1007```ts 1008class Balloon { 1009 volume: number; 1010 constructor(volume: number) { 1011 this.volume = volume; 1012 } 1013 1014 static increaseVolume(balloon:Balloon) { 1015 balloon.volume += 2; 1016 } 1017} 1018 1019@Entry 1020@Component 1021struct Index { 1022 @State balloon: Balloon = new Balloon(10); 1023 1024 reduceVolume(balloon:Balloon) { 1025 balloon.volume -= 1; 1026 } 1027 1028 build() { 1029 Column({space:8}) { 1030 Text(`The volume of the balloon is ${this.balloon.volume} cubic centimeters.`) 1031 .fontSize(30) 1032 Button(`increaseVolume`) 1033 .onClick(()=>{ 1034 // The UI cannot be re-rendered using a static method. 1035 Balloon.increaseVolume(this.balloon); 1036 }) 1037 Button(`reduceVolume`) 1038 .onClick(()=>{ 1039 // The UI cannot be re-rendered using this. 1040 this.reduceVolume(this.balloon); 1041 }) 1042 } 1043 .width('100%') 1044 .height('100%') 1045 } 1046} 1047``` 1048 1049You can add a proxy for **this.balloon** to re-render the UI by assigning a value to the variable and then calling the variable. 1050 1051[Example] 1052 1053```ts 1054class Balloon { 1055 volume: number; 1056 constructor(volume: number) { 1057 this.volume = volume; 1058 } 1059 1060 static increaseVolume(balloon:Balloon) { 1061 balloon.volume += 2; 1062 } 1063} 1064 1065@Entry 1066@Component 1067struct Index { 1068 @State balloon: Balloon = new Balloon(10); 1069 1070 reduceVolume(balloon:Balloon) { 1071 balloon.volume -= 1; 1072 } 1073 1074 build() { 1075 Column({space:8}) { 1076 Text(`The volume of the balloon is ${this.balloon.volume} cubic centimeters.`) 1077 .fontSize(30) 1078 Button(`increaseVolume`) 1079 .onClick(()=>{ 1080 // Add a proxy by assigning a value. 1081 let balloon1 = this.balloon; 1082 Balloon.increaseVolume(balloon1); 1083 }) 1084 Button(`reduceVolume`) 1085 .onClick(()=>{ 1086 // Add a proxy by assigning a value. 1087 let balloon2 = this.balloon; 1088 this.reduceVolume(balloon2); 1089 }) 1090 } 1091 .width('100%') 1092 .height('100%') 1093 } 1094} 1095``` 1096 1097### Changing State Variables Outside a Custom Component 1098 1099You 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. 1100 1101```ts 1102class Model { 1103 private callback: Function | undefined = () => {} 1104 1105 add(callback: () => void): void { 1106 this.callback = callback; 1107 } 1108 1109 delete(): void { 1110 this.callback = undefined; 1111 } 1112 1113 call(): void { 1114 if (this.callback) { 1115 this.callback(); 1116 } 1117 } 1118} 1119 1120let model: Model = new Model(); 1121 1122@Entry 1123@Component 1124struct Test { 1125 @State count: number = 10; 1126 1127 aboutToAppear(): void { 1128 model.add(() => { 1129 this.count++; 1130 }) 1131 } 1132 1133 build() { 1134 Column() { 1135 Text(`Value of count: ${this.count}`) 1136 Button('change') 1137 .onClick(() => { 1138 model.call(); 1139 }) 1140 } 1141 } 1142 1143 aboutToDisappear(): void { 1144 model.delete(); 1145 } 1146} 1147``` 1148 1149In addition, you can use [LocalStorage](./arkts-localstorage.md#changing-state-variables-outside-a-custom-component) to change the state variables outside a custom component. 1150