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![en-us_image_0000001502091796](figures/en-us_image_0000001502091796.png)
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![Video-state](figures/Video-state.gif)
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