1# \@Local Decorator: Representing the Internal State of Components
2
3You can use \@Local, a variable decorator in state management V2, to observe the variable changes in custom components decorated by \@ComponentV2.
4
5>**NOTE**
6>
7>The \@Local decorator is supported since API version 12.
8>
9
10## Overview
11
12\@Local indicates the internal state of a component, which enables the variables in the custom component to observe changes:
13
14- Variables decorated by \@Local cannot be initialized externally. They must be initialized inside the component.
15
16- When a variable decorated by \@Local changes, the component that uses the variable is re-rendered.
17
18- \@Local can observe basic types such as number, boolean, string, object, and class and built-in types such as Array, Set, Map, and Date.
19
20- \@Local can observe only the variable it decorates. If the decorated variable is of the simple type, it can observe value changes to the variable; if the decorated variable is of the object type, it can observe value changes to the entire object; if the decorated variable is of the array type, it can observe changes of the entire array and its items; if the decorated variable is of the built-in types, such as Array, Set, Map, and Date, it can observe changes caused by calling the APIs. For details, see [Observed Changes](#observed-changes).
21
22- \@Local supports null, undefined, and union types.
23
24## Limitations of the \@State decorator in State Management V1
25
26State management V1 uses the [\@State](arkts-state.md) decorator to define state variables in a class. However, because the \@State decorator allows variables to be initialized externally, it cannot accurately express the semantics that the internal state of the component cannot be modified externally.
27
28```ts
29class ComponentInfo {
30  name: string;
31  count: number;
32  message: string;
33  constructor(name: string, count: number, message: string) {
34    this.name = name;
35    this.count = count;
36    this.message = message;
37  }
38}
39@Component
40struct Child {
41  @State componentInfo: ComponentInfo = new ComponentInfo("Child", 1, "Hello World");
42
43  build() {
44    Column() {
45      Text(`componentInfo.message is ${this.componentInfo.message}`)
46    }
47  }
48}
49@Entry
50@Component
51struct Index {
52  build() {
53    Column() {
54      Child({componentInfo: new ComponentInfo("Unknown", 0, "Error")})
55    }
56  }
57}
58```
59
60In the preceding code, the initialization of the **Child** component can pass in a new value to overwrite the local value of **componentInfo** that the component wants to use as an internal state variable. However, the **Child** component cannot detect that **componentInfo** has been initialized externally, which is inconvenient for managing the internal state of the component. This is where \@Local, a decorator that represents the internal state of components, comes into the picture.
61
62## Decorator Description
63
64| \@Local Variable Decorator| Description|
65| ------------------- | ------------------------------------------------------------ |
66| Decorator parameters| None.|
67| Allowed variable types| Basic types, such as object, class, string, number, boolean, and enum, and built-in types such as Array, Date, Map, and Set. null, undefined, and union types.|
68| Initial value for the decorated variable| Local initialization is required. External initialization is not allowed.|
69
70## Variable Passing
71
72| Passing Rules      | Description                                                     |
73| -------------- | --------------------------------------------------------- |
74| Initialization from the parent component| Variables decorated by \@Local can only be initialized locally.|
75| Child component initialization  | Variables decorated by \@Local can initialize variables decorated by \@Param in the child components.   |
76
77## Observed Changes
78
79Variables decorated by \@Local are observable. When a decorated variable changes, the UI component bound to the variable will be re-rendered.
80
81- When the decorated variable is of boolean, string, or number type, value changes to the variable can be observed.
82
83  ```ts
84  @Entry
85  @ComponentV2
86  struct Index {
87    @Local count: number = 0;
88    @Local message: string = "Hello";
89    @Local flag: boolean = false;
90    build() {
91      Column() {
92        Text(`${this.count}`)
93        Text(`${this.message}`)
94        Text(`${this.flag}`)
95        Button("change Local")
96          .onClick(()=>{
97            // When @Local decorates a simple type, it can observe value changes to the variable.
98            this.count++;
99            this.message += " World";
100            this.flag = !this.flag;
101        })
102      }
103    }
104  }
105  ```
106
107- When the decorated variable is of a class object type, only the overall value changes to the class object can be observed. To observe value changes to the member properties in the class object, you'll need the \@ObservedV2 and \@Trace decorators. Note that \@Local cannot be used together with the instance objects of the \@Observed decorated class.
108
109    ```ts
110    class RawObject {
111      name: string;
112      constructor(name: string) {
113        this.name = name;
114      }
115    }
116    @ObservedV2
117    class ObservedObject {
118      @Trace name: string;
119      constructor(name: string) {
120        this.name = name;
121      }
122    }
123    @Entry
124    @ComponentV2
125    struct Index {
126      @Local rawObject: RawObject = new RawObject("rawObject");
127      @Local observedObject: ObservedObject = new ObservedObject("observedObject");
128      build() {
129        Column() {
130          Text(`${this.rawObject.name}`)
131          Text(`${this.observedObject.name}`)
132          Button("change object")
133            .onClick(() => {
134              // Value changes to the class object can be observed.
135              this.rawObject = new RawObject("new rawObject");
136              this.observedObject = new ObservedObject("new observedObject");
137          })
138          Button("change name")
139            .onClick(() => {
140              // @Local does not have the capability of observing class object property. Therefore, value changes of rawObject.name cannot be observed.
141              this.rawObject.name = "new rawObject name";
142              // The name property of ObservedObject is decorated by @Trace. Therefore, value changes of observedObject.name can be observed.
143              this.observedObject.name = "new observedObject name";
144          })
145        }
146      }
147    }
148    ```
149
150- When the decorated variable is of a simple array type, changes of the entire array or its items can be observed.
151
152    ```ts
153    @Entry
154    @ComponentV2
155    struct Index {
156      @Local numArr: number[] = [1,2,3,4,5];
157      @Local dimensionTwo: number[][] = [[1,2,3],[4,5,6]];
158
159      build() {
160        Column() {
161          Text(`${this.numArr[0]}`)
162          Text(`${this.numArr[1]}`)
163          Text(`${this.numArr[2]}`)
164          Text(`${this.dimensionTwo[0][0]}`)
165          Text(`${this.dimensionTwo[1][1]}`)
166          Button("change array item")
167            .onClick(() => {
168              this.numArr[0]++;
169              this.numArr[1] += 2;
170              this.dimensionTwo[0][0] = 0;
171              this.dimensionTwo[1][1] = 0;
172            })
173          Button("change whole array")
174            .onClick(() => {
175              this.numArr = [5,4,3,2,1];
176              this.dimensionTwo = [[7,8,9],[0,1,2]];
177            })
178        }
179      }
180    }
181    ```
182
183- When the decorated variable is of a nested type or an object array, changes of lower-level object properties cannot be observed. Observation of these lower-level object properties requires use of \@ObservedV2 and \@Trace decorators.
184
185  ```ts
186  @ObservedV2
187  class Region {
188    @Trace x: number;
189    @Trace y: number;
190    constructor(x: number, y: number) {
191      this.x = x;
192      this.y = y;
193    }
194  }
195  @ObservedV2
196  class Info {
197    @Trace region: Region;
198    @Trace name: string;
199    constructor(name: string, x: number, y: number) {
200      this.name = name;
201      this.region = new Region(x, y);
202    }
203  }
204  @Entry
205  @ComponentV2
206  struct Index {
207    @Local infoArr: Info[] = [new Info("Ocean", 28, 120), new Info("Mountain", 26, 20)];
208    @Local originInfo: Info = new Info("Origin", 0, 0);
209    build() {
210      Column() {
211        ForEach(this.infoArr, (info: Info) => {
212          Row() {
213            Text(`name: ${info.name}`)
214            Text(`region: ${info.region.x}-${info.region.y}`)
215          }
216        })
217        Row() {
218            Text(`Origin name: ${this.originInfo.name}`)
219            Text(`Origin region: ${this.originInfo.region.x}-${this.originInfo.region.y}`)
220        }
221        Button("change infoArr item")
222          .onClick(() => {
223            // Because the name property is decorated by @Trace, it can be observed.
224            this.infoArr[0].name = "Win";
225          })
226        Button("change originInfo")
227          .onClick(() => {
228            // Because the originInfo variable is decorated by @Local, it can be observed.
229            this.originInfo = new Info("Origin", 100, 100);
230          })
231        Button("change originInfo region")
232          .onClick(() => {
233            // Because the x and y properties are decorated by @Trace, it can be observed.
234            this.originInfo.region.x = 25;
235            this.originInfo.region.y = 25;
236          })
237      }
238    }
239  }
240  ```
241
242- When the decorated variable is of a built-in type, you can observe the overall value changes of the variable and the changes caused by calling the APIs listed below.
243
244  | Type | Observable APIs                                             |
245  | ----- | ------------------------------------------------------------ |
246  | Array | push, pop, shift, unshift, splice, copyWithin, fill, reverse, sort|
247  | Date  | setFullYear, setMonth, setDate, setHours, setMinutes, setSeconds, setMilliseconds, setTime, setUTCFullYear, setUTCMonth, setUTCDate, setUTCHours, setUTCMinutes, setUTCSeconds, setUTCMilliseconds |
248  | Map   | set, clear, delete                                           |
249  | Set   | add, clear, delete                                           |
250
251## Constraints
252
253The \@Local decorator has the following constraints:
254
255- The \@Local decorator can be used only in custom components decorated by \@ComponentV2.
256
257  ```ts
258  @ComponentV2
259  struct CompA {
260    @Local message: string = "Hello World"; // Correct usage.
261    build() {
262    }
263  }
264  @Component
265  struct CompB {
266    @Local message: string = "Hello World"; // Incorrect usage. An error is reported during compilation.
267    build() {
268    }
269  }
270  ```
271
272- The variable decorated by \@Local indicates the internal state of the component and cannot be initialized externally.
273
274  ```ts
275  @ComponentV2
276  struct CompA {
277    @Local message: string = "Hello World";
278    build() {
279    }
280  }
281  @ComponentV2
282  struct CompB {
283    build() {
284      CompA({ message: "Hello" }) // Incorrect usage. An error is reported during compilation.
285    }
286  }
287  ```
288
289## Comparison Between \@Local and \@State
290
291The following table compares the usage and functions of \@Local and \@State.
292
293|                    | \@State                      | \@Local                         |
294| ------------------ | ---------------------------- | --------------------------------- |
295| Parameter              | None.                         | None.                      |
296| Initialization from the parent component        | Optional.                 | External initialization is not allowed.          |
297| Observation capability| Variables and top-level member properties can be observed, but lower-level member properties cannot.| The variable itself can be observed. Lower-level observation requires use of \@Trace decorator.|
298| Data Transfer| It can be used as a data source to synchronize with the state variables in a child component.| It can be used as a data source to synchronize with the state variables in a child component.|
299
300## Use Scenarios
301
302### Observing Overall Changes of Objects
303
304When a class object and its properties are decorated by \@ObservedV2 and \@Trace, properties in the class object can be observed. However, value changes of the class object itself cannot be observed and do not initiate UI re-renders. In this case, you can use \@Local to decorate the object to observe the changes.
305
306```ts
307@ObservedV2
308class Info {
309  @Trace name: string;
310  @Trace age: number;
311  constructor(name: string, age: number) {
312    this.name = name;
313    this.age = age;
314  }
315}
316@Entry
317@ComponentV2
318struct Index {
319  info1: Info = new Info("Tom", 25);
320  @Local info2: Info = new Info("Tom", 25);
321  build() {
322    Column() {
323      Text(`info1: ${this.info1.name}-${this.info1.age}`) // Text1
324      Text(`info2: ${this.info2.name}-${this.info2.age}`) // Text2
325      Button("change info1&info2")
326        .onClick(() => {
327          this.info1 = new Info("Lucy", 18); // Text1 is not updated.
328          this.info2 = new Info("Lucy", 18); // Text2 is updated.
329      })
330    }
331  }
332}
333```
334
335### Decorating Variables of the Date Type
336
337When the decorated object is of the **Date** type, the overall value changes of **Date** can be observed. In addition, you can call the following APIs to update **Date** properties: **setFullYear**, **setMonth**, **setDate**, **setHours**, **setMinutes**, **setSeconds**, **setMilliseconds**, **setTime**, **setUTCFullYear**, **setUTCMonth**, **setUTCDate**, **setUTCHours**, **setUTCMinutes**, **setUTCSeconds**, and **setUTCMilliseconds**.
338
339```ts
340@Entry
341@ComponentV2
342struct DatePickerExample {
343  @Local selectedDate: Date = new Date('2021-08-08');
344
345  build() {
346    Column() {
347      Button('set selectedDate to 2023-07-08')
348        .margin(10)
349        .onClick(() => {
350          this.selectedDate = new Date('2023-07-08');
351        })
352      Button('increase the year by 1')
353        .margin(10)
354        .onClick(() => {
355          this.selectedDate.setFullYear(this.selectedDate.getFullYear() + 1);
356        })
357      Button('increase the month by 1')
358        .margin(10)
359        .onClick(() => {
360          this.selectedDate.setMonth(this.selectedDate.getMonth() + 1);
361        })
362      Button('increase the day by 1')
363        .margin(10)
364        .onClick(() => {
365          this.selectedDate.setDate(this.selectedDate.getDate() + 1);
366        })
367      DatePicker({
368        start: new Date('1970-1-1'),
369        end: new Date('2100-1-1'),
370        selected: this.selectedDate
371      })
372    }.width('100%')
373  }
374}
375```
376
377### Decorating Variables of the Map Type
378
379When the decorated object is of the **Map** type, the overall value changes of **Map** can be observed. In addition, you can call the set, clear, and delete interfaces to update the data in **Map**.
380
381```ts
382@Entry
383@ComponentV2
384struct MapSample {
385  @Local message: Map<number, string> = new Map([[0, "a"], [1, "b"], [3, "c"]]);
386
387  build() {
388    Row() {
389      Column() {
390        ForEach(Array.from(this.message.entries()), (item: [number, string]) => {
391          Text(`${item[0]}`).fontSize(30)
392          Text(`${item[1]}`).fontSize(30)
393          Divider()
394        })
395        Button('init map').onClick(() => {
396          this.message = new Map([[0, "a"], [1, "b"], [3, "c"]]);
397        })
398        Button('set new one').onClick(() => {
399          this.message.set(4, "d");
400        })
401        Button('clear').onClick(() => {
402          this.message.clear();
403        })
404        Button('replace the first one').onClick(() => {
405          this.message.set(0, "aa");
406        })
407        Button('delete the first one').onClick(() => {
408          this.message.delete(0);
409        })
410      }
411      .width('100%')
412    }
413    .height('100%')
414  }
415}
416```
417
418### Decorating Variables of the Set Type
419
420When the decorated object is **Set**, the overall value changes of **Set** can be observed. In addition, you can call the add, clear, and delete interfaces to update the data in **Set**.
421
422```ts
423@Entry
424@ComponentV2
425struct SetSample {
426  @Local message: Set<number> = new Set([0, 1, 2, 3, 4]);
427
428  build() {
429    Row() {
430      Column() {
431        ForEach(Array.from(this.message.entries()), (item: [number, string]) => {
432          Text(`${item[0]}`).fontSize(30)
433          Divider()
434        })
435        Button('init set').onClick(() => {
436          this.message = new Set([0, 1, 2, 3, 4]);
437        })
438        Button('set new one').onClick(() => {
439          this.message.add(5);
440        })
441        Button('clear').onClick(() => {
442          this.message.clear();
443        })
444        Button('delete the first one').onClick(() => {
445          this.message.delete(0);
446        })
447      }
448      .width('100%')
449    }
450    .height('100%')
451  }
452}
453```
454
455### Union Type
456
457\@Local supports null, undefined, and union types. In the following example, the **count** type is **number | undefined**. If you click to change the **count** type, the UI will be re-rendered accordingly.
458
459```ts
460@Entry
461@ComponentV2
462struct Index {
463  @Local count: number | undefined = 10;
464
465  build() {
466    Column() {
467      Text(`count(${this.count})`)
468      Button("change to undefined")
469        .onClick(() => {
470          this.count = undefined;
471        })
472      Button("change to number")
473        .onClick(() => {
474          this.count = 10;
475      })
476    }
477  }
478}
479```
480
481## FAQs
482
483### Repeated Value Changes to State Variables by Complex Constants Trigger Re-rendering
484
485```ts
486@Entry
487@ComponentV2
488struct Index {
489  list: string[][] = [['a'], ['b'], ['c']];
490  @Local dataObjFromList: string[] = this.list[0];
491
492  @Monitor("dataObjFromList")
493  onStrChange(monitor: IMonitor) {
494    console.log("dataObjFromList has changed");
495  }
496
497  build() {
498    Column() {
499      Button('change to self').onClick(() => {
500        // The new value is the same as the locally initialized value.
501        this.dataObjFromList = this.list[0];
502      })
503    }
504  }
505}
506```
507
508In the preceding example, each time you click Button('change to self'), the same constant of the **Array** type is assigned to a state variable of the same type, triggering re-rendering. This is because in state management V2, a proxy is added to Date, Map, Set, and Array that use state variable decorators such as @Trace and @Local to observe changes invoked by APIs.
509**dataObjFromList** is of a **Proxy** type but **list[0]** is of an **Array** type. As a result, when **list[0]** is assigned to **dataObjFromList**, the value changes trigger re-rendering.
510To avoid unnecessary value changes and re-renders, 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.
511
512Example of Using **UIUtils.getTarget()**
513
514```ts
515import { UIUtils } from '@ohos.arkui.StateManagement';
516
517@Entry
518@ComponentV2
519struct Index {
520  list: string[][] = [['a'], ['b'], ['c']];
521  @Local dataObjFromList: string[] = this.list[0];
522
523  @Monitor("dataObjFromList")
524  onStrChange(monitor: IMonitor) {
525    console.log("dataObjFromList has changed");
526  }
527
528  build() {
529    Column() {
530      Button('change to self').onClick(() => {
531        // Obtain the original value and compare it with the new value.
532        if (UIUtils.getTarget(this.dataObjFromList) !== this.list[0]) {
533          this.dataObjFromList = this.list[0];
534        }
535      })
536    }
537  }
538}
539```
540