• 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## 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
69  Declare the **Person** and **Model** classes.
70
71  ```ts
72  class Person {
73    public value: string;
74
75    constructor(value: string) {
76      this.value = value;
77    }
78  }
79
80  class Model {
81    public value: string;
82    public name: Person;
83    constructor(value: string, person: Person) {
84      this.value = value;
85      this.name = person;
86    }
87  }
88  ```
89
90  Use \@State to decorate a variable of the Model class object type.
91
92  ```ts
93  // Class type
94  @State title: Model = new Model('Hello', new Person('World'));
95  ```
96
97  Assign a value to the \@State decorated variable.
98
99  ```ts
100  // Assign a value to the class object.
101  this.title = new Model('Hi', new Person('ArkUI'));
102  ```
103
104  Assign a value to a property of the \@State decorated variable.
105
106  ```ts
107  // Assign a value to a property of the class object.
108  this.title.value = 'Hi';
109  ```
110
111  The value assignment of the nested property cannot be observed.
112
113  ```ts
114  // The value assignment of the nested property cannot be observed.
115  this.title.name.value = 'ArkUI';
116  ```
117- When the decorated variable is of the array type, the addition, deletion, and updates of array items can be observed. Below are some examples:
118  Declare the **Model** class.
119
120  ```ts
121  class Model {
122    public value: number;
123    constructor(value: number) {
124      this.value = value;
125    }
126  }
127  ```
128
129  Use \@State to decorate a variable of the Model class array type.
130
131  ```ts
132  // Array type
133  @State title: Model[] = [new Model(11), new Model(1)];
134  ```
135
136  The value assignment of the array itself can be observed.
137
138  ```ts
139  // Value assignment of the array
140  this.title = [new Model(2)];
141  ```
142
143  The value assignment of array items can be observed.
144
145  ```ts
146  // Value assignment of an array item
147  this.title[0] = new Model(2);
148  ```
149
150  The deletion of array items can be observed.
151
152  ```ts
153  // Array item change
154  this.title.pop();
155  ```
156
157  The addition of array items can be observed.
158
159  ```ts
160  // Array item change
161  this.title.push(new Model(12));
162  ```
163
164  The property value assignment in the array items cannot be observed.
165
166  ```ts
167  // The value assignment of the nested property cannot be observed.
168  this.title[0].value = 6;
169  ```
170
171- When the decorated variable is of the Date type, the overall value assignment of the **Date** object can be observed, and the following APIs can be called to update **Date** properties: **setFullYear**, **setMonth**, **setDate**, **setHours**, **setMinutes**, **setSeconds**, **setMilliseconds**, **setTime**, **setUTCFullYear**, **setUTCMonth**, **setUTCDate**, **setUTCHours**, **setUTCMinutes**, **setUTCSeconds**, and **setUTCMilliseconds**.
172
173  ```ts
174  @Entry
175  @Component
176  struct DatePickerExample {
177    @State selectedDate: Date = new Date('2021-08-08');
178
179    build() {
180      Column() {
181        Button('set selectedDate to 2023-07-08')
182          .margin(10)
183          .onClick(() => {
184            this.selectedDate = new Date('2023-07-08');
185          })
186        Button('increase the year by 1')
187          .margin(10)
188          .onClick(() => {
189            this.selectedDate.setFullYear(this.selectedDate.getFullYear() + 1);
190          })
191        Button('increase the month by 1')
192          .margin(10)
193          .onClick(() => {
194            this.selectedDate.setMonth(this.selectedDate.getMonth() + 1);
195          })
196        Button('increase the day by 1')
197          .margin(10)
198          .onClick(() => {
199            this.selectedDate.setDate(this.selectedDate.getDate() + 1);
200          })
201        DatePicker({
202          start: new Date('1970-1-1'),
203          end: new Date('2100-1-1'),
204          selected: this.selectedDate
205        })
206      }.width('100%')
207    }
208  }
209  ```
210
211- When the decorated variable is **Map**, value changes of **Map** can be observed. In addition, you can call the **set**, **clear**, and **delete** APIs of **Map** to update its value. For details, see [Decorating Variables of the Map Type](#decorating-variables-of-the-map-type).
212
213- When the decorated variable is **Set**, value changes of **Set** can be observed. In addition, you can call the **add**, **clear**, and **delete** APIs of **Set** to update its value. For details, see [Decorating Variables of the Set Type](#decorating-variables-of-the-set-type).
214
215### Framework Behavior
216
217- When a state variable is changed, the framework searches for components that depend on this state variable.
218
219- The framework executes an update method of the dependent components, which triggers re-rendering of the components.
220
221- Components or UI descriptions irrelevant to the state variable are not re-rendered, thereby implementing on-demand page updates.
222
223
224## Constraints
225
2261. Variables decorated by \@State must be initialized. Otherwise, an error is reported during compilation.
227
228  ```ts
229  // Incorrect format. An error is reported during compilation.
230  @State count: number;
231
232  // Correct format.
233  @State count: number = 10;
234  ```
235
2362. \@State cannot decorate variables of the function type. Otherwise, the framework throws a runtime error.
237
238
239## Application Scenarios
240
241
242### Decorating Variables of Simple Types
243
244In this example, \@State is used to decorate the **count** variable of the simple type, turning it into a state variable. The change of **count** causes the update of the **Button** component.
245
246- When **count** changes, the framework searches for components bound to it, which include only the **Button** component in this example.
247
248- The framework executes the update method of the **Button** component to implement on-demand update.
249
250
251```ts
252@Entry
253@Component
254struct MyComponent {
255  @State count: number = 0;
256
257  build() {
258    Button(`click times: ${this.count}`)
259      .onClick(() => {
260        this.count += 1;
261      })
262  }
263}
264```
265
266
267### Decorating Variables of the Class Object Type
268
269- In this example, \@State is used to decorate the variables **count** and **title** in the custom component **MyComponent**. The type of **title** is **Model**, a custom class. If the value of **count** or **title** changes, the framework searches for all **MyComponent** instances that depend on these variables and triggers re-rendering of them.
270
271- The **EntryComponent** has multiple **MyComponent** instances. The internal state change of the first **MyComponent** instance does not affect the second **MyComponent** instance.
272
273```ts
274class Model {
275  public value: string;
276
277  constructor(value: string) {
278    this.value = value;
279  }
280}
281
282@Entry
283@Component
284struct EntryComponent {
285  build() {
286    Column() {
287      // The parameters specified here will overwrite the default values defined locally during initial render. Not all parameters need to be initialized from the parent component.
288      MyComponent({ count: 1, increaseBy: 2 })
289        .width(300)
290      MyComponent({ title: new Model('Hello World 2'), count: 7 })
291    }
292  }
293}
294
295@Component
296struct MyComponent {
297  @State title: Model = new Model('Hello World');
298  @State count: number = 0;
299  private increaseBy: number = 1;
300
301  build() {
302    Column() {
303      Text(`${this.title.value}`)
304        .margin(10)
305      Button(`Click to change title`)
306        .onClick(() => {
307          // The update of the @State decorated variable triggers the update of the <Text> component.
308          this.title.value = this.title.value === 'Hello ArkUI' ? 'Hello World' : 'Hello ArkUI';
309        })
310        .width(300)
311        .margin(10)
312
313      Button(`Click to increase count = ${this.count}`)
314        .onClick(() => {
315          // The update of the @State decorated variable triggers the update of the <Button> component.
316          this.count += this.increaseBy;
317        })
318        .width(300)
319        .margin(10)
320    }
321  }
322}
323```
324
325![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  build() {
816    Column() {
817      Text(this.dataObj.name).fontSize(30)
818    }
819  }
820}
821```
822
823In the preceding example, each time you click **Button('change to self')**, the same class constant is assigned to a state variable of the **Class** type, triggering re-rendering. In state management V1, a proxy is added to the @Observed decorated class objects and the @State decorated objects of the Class, Date, Map, Set, or Array type to observe the changes of top-level properties or API invoking.
824**dataObjFromList** is of a **Proxy** type but **list[0]** is of an **Object** type. As a result, when **list[0]** is assigned to **dataObjFromList**, the value changes trigger re-rendering.
825To avoid unnecessary value changes and re-renders, use \@Observed to decorate the class, or use [UIUtils.getTarget()](./arkts-new-getTarget.md) to obtain the original value and determine whether the original and new values are the same. If they are the same, do not perform value changes.
826Method 1: Add \@Observed decorator.
827
828```ts
829@Observed
830class DataObj {
831  name: string = 'default name';
832
833  constructor(name: string) {
834    this.name = name;
835  }
836}
837
838@Entry
839@Component
840struct Index {
841  list: DataObj[] = [new DataObj('a'), new DataObj('b'), new DataObj('c')];
842  @State dataObjFromList: DataObj = this.list[0];
843
844  build() {
845    Column() {
846      ConsumerChild({ dataObj: this.dataObjFromList })
847      Button('change to self').onClick(() => {
848        this.dataObjFromList = this.list[0];
849      })
850    }
851  }
852}
853
854@Component
855struct ConsumerChild {
856  @Link @Watch('onDataObjChange') dataObj: DataObj;
857
858  onDataObjChange() {
859    console.log("dataObj changed");
860  }
861
862  build() {
863    Column() {
864      Text(this.dataObj.name).fontSize(30)
865    }
866  }
867}
868```
869
870In the preceding example, the \@Observed decorator is added to decorate the class, **list[0]** is of the **Proxy** type. In this case, when a value is reassigned, the same object will not be re-rendered.
871
872Method 2: Use [UIUtils.getTarget()](./arkts-new-getTarget.md) to obtain the original object.
873
874```ts
875import { UIUtils } from '@ohos.arkui.StateManagement';
876
877class DataObj {
878  name: string = 'default name';
879
880  constructor(name: string) {
881    this.name = name;
882  }
883}
884
885@Entry
886@Component
887struct Index {
888  list: DataObj[] = [new DataObj('a'), new DataObj('b'), new DataObj('c')];
889  @State dataObjFromList: DataObj = this.list[0];
890
891  build() {
892    Column() {
893      ConsumerChild({ dataObj: this.dataObjFromList })
894      Button('change to self').onClick(() => {
895        // Obtain the original value and compare it with the new value.
896        if (UIUtils.getTarget(this.dataObjFromList) !== this.list[0]) {
897          this.dataObjFromList = this.list[0];
898        }
899      })
900    }
901  }
902}
903
904@Component
905struct ConsumerChild {
906  @Link @Watch('onDataObjChange') dataObj: DataObj;
907
908  onDataObjChange() {
909    console.log("dataObj changed");
910  }
911
912  build() {
913    Column() {
914      Text(this.dataObj.name).fontSize(30)
915    }
916  }
917}
918```
919
920In the preceding example, **getTarget** is used to obtain the original value of the corresponding state variable before value change. After comparison, if the original value is the same as the new value, re-rendering will not be triggered.
921
922### Changing State Variables in build() Is Forbidden
923
924State variables cannot be changed in **build()**. Otherwise, the state management framework reports error logs during runtime.
925
926The rendering process is as follows:
927
9281. Create a custom component in **Index**.
929
9302. Execute the **build** method of **Index** as follows:
931
932    1. Create a **Column** component.
933
934    2. Create a Text component. **This.count++** is triggered when the **Text** component is created.
935
936    3. The value change of **count** triggers the re-render of the **Text** component.
937
938    4. Return value of **Text** is 2.
939
940```ts
941@Entry
942@Component
943struct Index {
944  @State count: number = 1;
945
946  build() {
947    Column() {
948      // Avoid directly changing the value of count in the Text component.
949      Text(`${this.count++}`)
950        .width(50)
951        .height(50)
952    }
953  }
954}
955```
956
957During the first creation, the **Text** component is rendered twice. As a result, return value of the **Text** component is **2**.
958
959If the framework identifies that the state variable is changed in **build()**, an error log is generated. The error log is as follows:
960
961```ts
962FIX THIS APPLICATION ERROR: @Component 'Index'[4]: State variable 'count' has changed during render! It's illegal to change @Component state while build (initial render or re-render) is on-going. Application error!
963```
964
965In the preceding example, this error does not cause serious consequences, for only the **Text** component is rendered one more time. Therefore, you may ignore this log.
966
967However, this behavior is a serious mistake. As the project becomes more complex, the potential risk becomes more and more serious.<br> Example:
968
969```ts
970@Entry
971@Component
972struct Index {
973  @State message: number = 20;
974
975  build() {
976    Column() {
977      Text(`${this.message++}`)
978
979      Text(`${this.message++}`)
980    }
981    .height('100%')
982    .width('100%')
983  }
984}
985```
986The rendering process in the preceding example is as follows:
987
9881. Create the first **Text** component to trigger the change of **this.message**.
989
9902. The change of **this.message** triggers the re-render of the second **Text** component.
991
9923. The re-render of the second **Text** component triggers the change of **this.message**, which again triggers the re-render of the first **Text** component.
993
9944. Re-render is performed repeatedly.
995
9965. The system does not respond for a long time, causing an App Freeze.
997
998Therefore, you are not advised to change the state variables in **build**. When the error "FIX THIS APPLICATION ERROR: @Component ... has changed during render! It's illegal to change @Component state while build (initial render or re-render) is on-going. Application error!" is reported, even if it does not bring serious consequences for now, you should pay attention to. Checking the application and modifying the corresponding error code to clear the error log are recommended.
999
1000### Using the a.b(this.object) Format Fails to Trigger UI Re-render
1001
1002In the **build** method, when the variable decorated by \@State is of the object type and is called in the **a.b(this.object)** format, the native object of **this.object** is passed in the b method. If the property of **this.object** is changed, the UI cannot be re-rendered. In the following example, when the static method **Balloon.increaseVolume** or **this.reduceVolume** is used to change the **volume** of **Balloon**, the UI is not re-rendered.
1003
1004[Incorrect Usage]
1005
1006```ts
1007class Balloon {
1008  volume: number;
1009  constructor(volume: number) {
1010    this.volume = volume;
1011  }
1012
1013  static increaseVolume(balloon:Balloon) {
1014    balloon.volume += 2;
1015  }
1016}
1017
1018@Entry
1019@Component
1020struct Index {
1021  @State balloon: Balloon = new Balloon(10);
1022
1023  reduceVolume(balloon:Balloon) {
1024    balloon.volume -= 1;
1025  }
1026
1027  build() {
1028    Column({space:8}) {
1029      Text(`The volume of the balloon is ${this.balloon.volume} cubic centimeters.`)
1030        .fontSize(30)
1031      Button(`increaseVolume`)
1032        .onClick(()=>{
1033          // The UI cannot be re-rendered using a static method.
1034          Balloon.increaseVolume(this.balloon);
1035        })
1036      Button(`reduceVolume`)
1037        .onClick(()=>{
1038          // The UI cannot be re-rendered using this.
1039          this.reduceVolume(this.balloon);
1040        })
1041    }
1042    .width('100%')
1043    .height('100%')
1044  }
1045}
1046```
1047
1048You can add a proxy for **this.balloon** to re-render the UI by assigning a value to the variable and then calling the variable.
1049
1050[Example]
1051
1052```ts
1053class Balloon {
1054  volume: number;
1055  constructor(volume: number) {
1056    this.volume = volume;
1057  }
1058
1059  static increaseVolume(balloon:Balloon) {
1060    balloon.volume += 2;
1061  }
1062}
1063
1064@Entry
1065@Component
1066struct Index {
1067  @State balloon: Balloon = new Balloon(10);
1068
1069  reduceVolume(balloon:Balloon) {
1070    balloon.volume -= 1;
1071  }
1072
1073  build() {
1074    Column({space:8}) {
1075      Text(`The volume of the balloon is ${this.balloon.volume} cubic centimeters.`)
1076        .fontSize(30)
1077      Button(`increaseVolume`)
1078        .onClick(()=>{
1079          // Add a proxy by assigning a value.
1080          let balloon1 = this.balloon;
1081          Balloon.increaseVolume(balloon1);
1082        })
1083      Button(`reduceVolume`)
1084        .onClick(()=>{
1085          // Add a proxy by assigning a value.
1086          let balloon2 = this.balloon;
1087          this.reduceVolume(balloon2);
1088        })
1089    }
1090    .width('100%')
1091    .height('100%')
1092  }
1093}
1094```
1095
1096### Changing State Variables Outside a Custom Component
1097
1098You can register the arrow function in **aboutToAppear** to change the state variables in the component. Note that the registered function must be left empty in **aboutToDisappear**. Otherwise, the custom component cannot be released because the arrow function captures the **this** instance of the component, causing memory leakage.
1099
1100```ts
1101class Model {
1102  private callback: Function | undefined = () => {}
1103
1104  add(callback: () => void): void {
1105    this.callback = callback;
1106  }
1107
1108  delete(): void {
1109    this.callback = undefined;
1110  }
1111
1112  call(): void {
1113    if (this.callback) {
1114      this.callback();
1115    }
1116  }
1117}
1118
1119let model: Model = new Model();
1120
1121@Entry
1122@Component
1123struct Test {
1124  @State count: number = 10;
1125
1126  aboutToAppear(): void {
1127    model.add(() => {
1128      this.count++;
1129    })
1130  }
1131
1132  build() {
1133    Column() {
1134      Text(`Value of count: ${this.count}`)
1135      Button('change')
1136        .onClick(() => {
1137          model.call();
1138        })
1139    }
1140  }
1141
1142  aboutToDisappear(): void {
1143    model.delete();
1144  }
1145}
1146```
1147
1148In addition, you can use [LocalStorage](./arkts-localstorage.md#changing-state-variables-outside-a-custom-component) to change the state variables outside a custom component.
1149