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