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