1# LocalStorage: Storing UI State
2
3
4LocalStorage provides storage for the page-level UI state. The parameters of the LocalStorage type accepted through the \@Entry decorator share the same LocalStorage instance on the page. LocalStorage also allows for state sharing between pages with UIAbility instances.
5
6
7This topic describes only the LocalStorage application scenarios and related decorators: \@LocalStorageProp and \@LocalStorageLink.
8
9
10Before reading this topic, you are advised to read [State Management Overview](./arkts-state-management-overview.md) to have a basic understanding of the state management framework.
11
12LocalStorage also provides APIs for you to manually add, delete, change, and query keys of Storage outside the custom component. You are advised to read this topic together with [LocalStorage API reference](../reference/apis-arkui/arkui-ts/ts-state-management.md#localstorage9).
13
14> **NOTE**
15>
16> LocalStorage is supported since API version 9.
17
18
19## Overview
20
21LocalStorage is an in-memory "database" that ArkTS provides for storing state variables required to build pages of the application UI.
22
23- An application can create multiple LocalStorage instances. These instances can be shared on a page or, by using the **getShared** API, across pages in a UIAbility instance.
24
25- The root node of a component tree, that is, the \@Component decorated by \@Entry, can be assigned to a LocalStorage instance. All child instances of this custom component automatically gain access to the same LocalStorage instance.
26
27- The \@Component decorated components can automatically inherit the LocalStorage instance from the parent component or receive the specified LocalStorage instance. For details, see [Example of Providing a Custom Component with Access to a LocalStorage Instance](#example-of-providing-a-custom-component-with-access-to-a-localstorage-instance).
28
29- All attributes in LocalStorage are mutable.
30
31The application determines the lifecycle of a LocalStorage object. The JS Engine will garbage collect a LocalStorage object when the application releases the last reference to it, which includes deleting the last custom component.
32
33LocalStorage provides two decorators based on the synchronization type of the component decorated with \@Component:
34
35- [@LocalStorageProp](#localstorageprop): creates a one-way data synchronization with the named attribute in LocalStorage.
36
37- [@LocalStorageLink](#localstoragelink): creates a two-way data synchronization with the named attribute in LocalStorage.
38
39
40## \@LocalStorageProp
41
42As mentioned above, if you want to establish a binding between LocalStorage and a custom component, you need to use the \@LocalStorageProp and \@LocalStorageLink decorators. Specially, use \@LocalStorageProp(key) or \@LocalStorageLink(key) to decorate variables in the component, where **key** identifies the attribute in LocalStorage.
43
44
45When a custom component is initialized, the \@LocalStorageProp(key)/\@LocalStorageLink(key) decorated variable is initialized with the value of the attribute with the given key in LocalStorage. Local initialization is mandatory. If an attribute with the given key is missing from LocalStorage, it will be added with the stated initializing value. (Whether the attribute with the given key exists in LocalStorage depends on the application logic.)
46
47
48> **NOTE**
49>
50> This decorator can be used in ArkTS widgets since API version 9.
51>
52> This decorator can be used in atomic services since API version 11.
53
54By decorating a variable with \@LocalStorageProp(key), a one-way data synchronization is established from the attribute with the given key in LocalStorage to the variable. This means that, local changes (if any) will not be synchronized to LocalStorage, and an update to the attribute with the given key in LocalStorage – for example, a change made with the **set** API – will overwrite local changes.
55
56
57### Rules of Use
58
59| \@LocalStorageProp Decorator| Description                                      |
60| ----------------------- | ---------------------------------------- |
61| Decorator parameters                  | **key**: constant string, mandatory (the string must be quoted)                 |
62| Allowed variable types              | Object, class, string, number, Boolean, enum, and array of these types.<br>(Applicable to API version 12 or later) Map, Set, and Date types. For details about the scenarios of nested objects, see [Observed Changes and Behavior](#observed-changes-and-behavior).<br>The type must be specified. Whenever possible, use the same type as that of the corresponding attribute in LocalStorage. Otherwise, implicit type conversion occurs, causing application behavior exceptions.<br>**any** is not supported. **undefined** and **null** are supported since API version 12.<br>(Applicable to API version 12 or later) Union type of the preceding types, for example, **string \| number**, **string \| undefined** or **ClassA \| null**. For details, see [Union Type @LocalStorage](#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, **@LocalStorageProp("AA") a: number \| null = null** is recommended; **@LocalStorageProp("AA") a: number = null** is not recommended.|
63| Synchronization type                   | One-way: from the attribute in LocalStorage to the component variable. The component variable can be changed locally, but an update from LocalStorage will overwrite local changes.|
64| Initial value for the decorated variable              | Mandatory. If the attribute does not exist in LocalStorage, it will be created and initialized with this value.|
65
66
67### Variable Transfer/Access Rules
68
69| Transfer/Access     | Description                                      |
70| ---------- | ---------------------------------------- |
71| Initialization and update from the parent component| Forbidden.|
72| Child component initialization    | Supported. The \@LocalStorageProp decorated variable can be used to initialize an \@State, \@Link, \@Prop, or \@Provide decorated variable in the child component.|
73| Access from outside the component | Not supported.                                      |
74
75  **Figure 1** \@LocalStorageProp initialization rule
76
77![en-us_image_0000001501936014](figures/en-us_image_0000001501936014.png)
78
79
80### Observed Changes and Behavior
81
82**Observed Changes**
83
84
85- When the decorated variable is of the Boolean, string, or number type, its value change can be observed.
86
87- When the decorated variable is of the class or object type, its value change as well as value changes of all its attributes can be observed. For details, see [Example for Using LocalStorage from Inside the UI](#example-for-using-localstorage-from-inside-the-ui).
88
89- When the decorated variable is of the array type, the addition, deletion, and updates of array items can be observed.
90
91- When 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**. For details, see [Decorating Variables of the Date Type](#decorating-variables-of-the-date-type).
92
93- 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).
94
95- 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).
96
97
98**Framework Behavior**
99
100
101- Value changes of the variables decorated by \@LocalStorageProp are not synchronized to LocalStorage.
102
103- Value changes of the variables decorated by \@LocalStorageProp will cause a re-render of components associated with the current custom component.
104
105- When an attribute with the given key in LocalStorage is updated, the change is synchronized to all the \@LocalStorageProp(key) decorated variables and overwrite all local changes of these variables.
106
107![LocalStorageProp_framework_behavior](figures/LocalStorageProp_framework_behavior.png)
108
109
110## \@LocalStorageLink
111
112> **NOTE**
113>
114> This decorator can be used in atomic services since API version 11.
115
116\@LocalStorageLink is required if you need to synchronize the changes of the state variables in a custom component back to LocalStorage.
117
118\@LocalStorageLink(key) creates a two-way data synchronization with the attribute with the given key in LocalStorage.
119
1201. If a local change occurs, it is synchronized to LocalStorage.
121
1222. Changes in LocalStorage are synchronized to all attributes with the given key, including one-way bound variables (\@LocalStorageProp decorated variables and one-way bound variables created through \@Prop) and two-way bound variables (\@LocalStorageLink decorated variables and two-way bound variables created through \@Link).
123
124### Rules of Use
125
126| \@LocalStorageLink Decorator| Description                                      |
127| ----------------------- | ---------------------------------------- |
128| Decorator parameters                  | **key**: constant string, mandatory (the string must be quoted)                 |
129| Allowed variable types              | Object, class, string, number, Boolean, enum, and array of these types.<br>(Applicable to API version 12 or later) Map, Set, and Date types. For details about the scenarios of nested objects, see [Observed Changes and Behavior](#observed-changes-and-behavior).<br>The type must be specified. Whenever possible, use the same type as that of the corresponding attribute in LocalStorage. Otherwise, implicit type conversion occurs, causing application behavior exceptions.<br>**any** is not supported. **undefined** and **null** are supported since API version 12.<br>(Applicable to API version 12 or later) Union type of the preceding types, for example, **string \| number**, **string \| undefined** or **ClassA \| null**. For details, see [Union Type @LocalStorage](#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, **@LocalStorageLink("AA") a: number \| null = null** is recommended. **@LocalStorageLink("AA") a: number = null** is not recommended.|
130| Synchronization type                   | Two-way: from the attribute in LocalStorage to the custom component variable and back|
131| Initial value for the decorated variable              | Mandatory. If the attribute does not exist in LocalStorage, it will be created and initialized with this value.|
132
133
134### Variable Transfer/Access Rules
135
136| Transfer/Access     | Description                                      |
137| ---------- | ---------------------------------------- |
138| Initialization and update from the parent component| Forbidden.|
139| Child component initialization    | Supported. The \@LocalStorageProp decorated variable can be used to initialize an \@State, \@Link, \@Prop, or \@Provide decorated variable in the child component.|
140| Access from outside the component | Not supported.                                      |
141
142
143  **Figure 2** \@LocalStorageLink initialization rule
144
145
146![en-us_image_0000001552855957](figures/en-us_image_0000001552855957.png)
147
148
149### Observed Changes and Behavior
150
151**Observed Changes**
152
153
154- When the decorated variable is of the Boolean, string, or number type, its value change can be observed.
155
156- When the decorated variable is of the class or object type, its value change as well as value changes of all its attributes can be observed. For details, see [Example for Using LocalStorage from Inside the UI](#example-for-using-localstorage-from-inside-the-ui).
157
158- When the decorated variable is of the array type, the addition, deletion, and updates of array items can be observed.
159
160- When 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**. For details, see [Decorating Variables of the Date Type](#decorating-variables-of-the-date-type).
161
162- 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).
163
164- 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).
165
166
167**Framework Behavior**
168
169
1701. When the value change of the \@LocalStorageLink(key) decorated variable is observed, the change is synchronized to the attribute with the give key value in LocalStorage.
171
1722. Once the attribute with the given key in LocalStorage is updated, all the data (including \@LocalStorageLink and \@LocalStorageProp decorated variables) bound to the attribute key is changed synchronously.
173
1743. When the data decorated by \@LocalStorageLink(key) is a state variable, the change of the data is synchronized to LocalStorage, and the owning custom component is re-rendered.
175
176![LocalStorageLink_framework_behavior](figures/LocalStorageLink_framework_behavior.png)
177
178
179## Constraints
180
1811. The parameter of \@LocalStorageProp and \@LocalStorageLink must be of the string type. Otherwise, an error is reported during compilation.
182
183```ts
184let storage = new LocalStorage();
185storage.setOrCreate('PropA', 48);
186
187// Incorrect format. An error is reported during compilation.
188@LocalStorageProp() localStorageProp: number = 1;
189@LocalStorageLink() localStorageLink: number = 2;
190
191// Correct format.
192@LocalStorageProp('PropA') localStorageProp: number = 1;
193@LocalStorageLink('PropA') localStorageLink: number = 2;
194```
195
1962. \@StorageProp and \@StorageLink cannot decorate variables of the function type. Otherwise, the framework throws a runtime error.
197
1983. Once created, a named attribute cannot have its type changed. Subsequent calls to **Set** must set a value of same type.
199
2004. LocalStorage provides page-level storage. The [getShared](../reference/apis-arkui/arkui-ts/ts-state-management.md#getshared10) API can only obtain the LocalStorage instance passed through [windowStage.loadContent](../reference/apis-arkui/js-apis-window.md#loadcontent9) in the current stage. If the instance is not available, **undefined** is returned. For the example, see [Example of Sharing a LocalStorage Instance from UIAbility to One or More Pages](#example-of-sharing-a-localstorage-instance-from-uiability-to-one-or-more-pages).
201
202
203## Use Scenarios
204
205
206### Example of Using LocalStorage in Application Logic
207
208
209```ts
210let para: Record<string,number> = { 'PropA': 47 };
211let storage: LocalStorage = new LocalStorage(para); // Create an instance and initialize it with the given object.
212let propA: number | undefined = storage.get('PropA'); // propA == 47
213let link1: SubscribedAbstractProperty<number> = storage.link('PropA'); // link1.get() == 47
214let link2: SubscribedAbstractProperty<number> = storage.link('PropA'); // link2.get() == 47
215let prop: SubscribedAbstractProperty<number> = storage.prop('PropA'); // prop.get() == 47
216link1.set(48); // Two-way synchronization: link1.get() == link2.get() == prop.get() == 48
217prop.set(1); // One-way synchronization: prop.get() == 1; but link1.get() == link2.get() == 48
218link1.set(49); // Two-way synchronization: link1.get() == link2.get() == prop.get() == 49
219```
220
221
222### Example for Using LocalStorage from Inside the UI
223
224The two decorators \@LocalStorageProp and \@LocalStorageLink can work together to obtain the state variable stored in a LocalStorage instance in the UI component.
225
226This example uses \@LocalStorageLink to implement the following:
227
228- Use the **build** function to create a LocalStorage instance named **storage**.
229
230- Use the \@Entry decorator to add **storage** to the top-level component **Parent**.
231
232- Use \@LocalStorageLink to create a two-way data synchronization with the given attribute in LocalStorage.
233
234 ```ts
235class Data {
236  code: number;
237
238  constructor(code: number) {
239    this.code = code;
240  }
241}
242// Create a new instance and initialize it with the given object.
243let para: Record<string, number> = { 'PropA': 47 };
244let storage: LocalStorage = new LocalStorage(para);
245storage.setOrCreate('PropB', new Data(50));
246
247@Component
248struct Child {
249  // @LocalStorageLink creates a two-way data synchronization with the PropA attribute in LocalStorage.
250  @LocalStorageLink('PropA') childLinkNumber: number = 1;
251  // @LocalStorageLink creates a two-way data synchronization with the PropB attribute in LocalStorage.
252  @LocalStorageLink('PropB') childLinkObject: Data = new Data(0);
253
254  build() {
255    Column({ space: 15 }) {
256      Button(`Child from LocalStorage ${this.childLinkNumber}`) // The changes will be synchronized to PropA in LocalStorage and with Parent.parentLinkNumber.
257        .onClick(() => {
258          this.childLinkNumber += 1;
259        })
260
261      Button(`Child from LocalStorage ${this.childLinkObject.code}`) // The changes will be synchronized to PropB in LocalStorage and with Parent.parentLinkObject.code.
262        .onClick(() => {
263          this.childLinkObject.code += 1;
264        })
265    }
266  }
267}
268// Make LocalStorage accessible from the @Component decorated component.
269@Entry(storage)
270@Component
271struct Parent {
272  // @LocalStorageLink creates a two-way data synchronization with the PropA attribute in LocalStorage.
273  @LocalStorageLink('PropA') parentLinkNumber: number = 1;
274  // @LocalStorageLink creates a two-way data synchronization with the PropB attribute in LocalStorage.
275  @LocalStorageLink('PropB') parentLinkObject: Data = new Data(0);
276
277  build() {
278    Column({ space: 15 }) {
279      Button(`Parent from LocalStorage ${this.parentLinkNumber}`) // The value of this.parentLinkNumber is 47 because PropA in LocalStorage has been initialized.
280        .onClick(() => {
281          this.parentLinkNumber += 1;
282        })
283
284      Button(`Parent from LocalStorage ${this.parentLinkObject.code}`) // The value of this.parentLinkObject.code is 50 because PropB in LocalStorage has been initialized.
285        .onClick(() => {
286          this.parentLinkObject.code += 1;
287        })
288      // The @Component decorated child component automatically obtains access to the Parent LocalStorage instance.
289      Child()
290    }
291  }
292}
293 ```
294
295
296### Simple Example of Using \@LocalStorageProp with LocalStorage
297
298In this example, the **Parent** and **Child** components create local data that is one-way synchronized with the PropA attribute in the LocalStorage instance **storage**.
299
300- The change of **this.storageProp1** in **Parent** takes effect only in **Parent** and is not synchronized to **storage**.
301
302- In the **Child** component, the value of **storageProp2** bound to **Text** is still 47.
303
304```ts
305// Create a new instance and initialize it with the given object.
306let para: Record<string, number> = { 'PropA': 47 };
307let storage: LocalStorage = new LocalStorage(para);
308// Make LocalStorage accessible from the @Component decorated component.
309@Entry(storage)
310@Component
311struct Parent {
312  // @LocalStorageProp creates a one-way data synchronization with the PropA attribute in LocalStorage.
313  @LocalStorageProp('PropA') storageProp1: number = 1;
314
315  build() {
316    Column({ space: 15 }) {
317      // The initial value is 47. After the button is clicked, the value is incremented by 1. The change takes effect only in storageProp1 in the current component and is not synchronized to LocalStorage.
318      Button(`Parent from LocalStorage ${this.storageProp1}`)
319        .onClick(() => {
320          this.storageProp1 += 1;
321        })
322      Child()
323    }
324  }
325}
326
327@Component
328struct Child {
329  // @LocalStorageProp creates a one-way data synchronization with the PropA attribute in LocalStorage.
330  @LocalStorageProp('PropA') storageProp2: number = 2;
331
332  build() {
333    Column({ space: 15 }) {
334      // When Parent changes, the current storageProp2 does not change, and 47 is displayed.
335      Text(`Parent from LocalStorage ${this.storageProp2}`)
336    }
337  }
338}
339```
340
341
342### Simple Example of Using \@LocalStorageLink with LocalStorage
343
344This example shows how to create a two-way data synchronization between an \@LocalStorageLink decorated variable and LocalStorage.
345
346
347```ts
348// Create a LocalStorage instance.
349let para: Record<string, number> = { 'PropA': 47 };
350let storage: LocalStorage = new LocalStorage(para);
351// Call the link API (available since API version 9) to create a two-way data synchronization with PropA. linkToPropA is a global variable.
352let linkToPropA: SubscribedAbstractProperty<object> = storage.link('PropA');
353
354@Entry(storage)
355@Component
356struct Parent {
357
358  // @LocalStorageLink('PropA') creates a two-way synchronization with PropA in the Parent custom component. The initial value is 47, because PropA has been set to 47 during LocalStorage construction.
359  @LocalStorageLink('PropA') storageLink: number = 1;
360
361  build() {
362    Column() {
363      Text(`incr @LocalStorageLink variable`)
364        // Clicking incr @LocalStorageLink variable increases the value of this.storageLink by 1. The change is synchronized back to the storage. The global variable linkToPropA also changes.
365
366        .onClick(() => {
367          this.storageLink += 1;
368        })
369
370      // Avoid using the global variable linkToPropA.get() in the component. Doing so may cause errors due to different lifecycles.
371      Text(`@LocalStorageLink: ${this.storageLink} - linkToPropA: ${linkToPropA.get()}`)
372    }
373  }
374}
375```
376
377
378### Example of Syncing State Variables Between Sibling Components
379
380This example shows how to use \@LocalStorageLink to create a two-way synchronization for the state between sibling components.
381
382Check the changes in the **Parent** custom component.
383
3841. Clicking **playCount ${this.playCount} dec by 1** decreases the value of **this.playCount** by 1. This change is synchronized to LocalStorage and to the components bound to **playCountLink** in the **Child** component.
385
3862. Click **countStorage ${this.playCount} incr by 1** to call the **set** API in LocalStorage to update the attributes corresponding to **countStorage** in LocalStorage. The components bound to** playCountLink** in the **Child** component are updated synchronously.
387
3883. The **playCount in LocalStorage for debug ${storage.get&lt;number&gt;('countStorage')}** **Text** component is not updated synchronously, because **storage.get&lt;number&gt;('countStorage')** returns a regular variable. The update of a regular variable does not cause the **Text** component to be re-rendered.
389
390Changes in the **Child** custom component:
391
3921. The update of **playCountLink** is synchronized to LocalStorage, and the parent and sibling child custom components are re-rendered accordingly.
393
394```ts
395let count: Record<string, number> = { 'countStorage': 1 };
396let storage: LocalStorage = new LocalStorage(count);
397
398@Component
399struct Child {
400  // Name the child component instance.
401  label: string = 'no name';
402  // Two-way synchronization with countStorage in LocalStorage.
403  @LocalStorageLink('countStorage') playCountLink: number = 0;
404
405  build() {
406    Row() {
407      Text(this.label)
408        .width(50).height(60).fontSize(12)
409      Text(`playCountLink ${this.playCountLink}: inc by 1`)
410        .onClick(() => {
411          this.playCountLink += 1;
412        })
413        .width(200).height(60).fontSize(12)
414    }.width(300).height(60)
415  }
416}
417
418@Entry(storage)
419@Component
420struct Parent {
421  @LocalStorageLink('countStorage') playCount: number = 0;
422
423  build() {
424    Column() {
425      Row() {
426        Text('Parent')
427          .width(50).height(60).fontSize(12)
428        Text(`playCount ${this.playCount} dec by 1`)
429          .onClick(() => {
430            this.playCount -= 1;
431          })
432          .width(250).height(60).fontSize(12)
433      }.width(300).height(60)
434
435      Row() {
436        Text('LocalStorage')
437          .width(50).height(60).fontSize(12)
438        Text(`countStorage ${this.playCount} incr by 1`)
439          .onClick(() => {
440            storage.set<number | undefined>('countStorage', Number(storage.get<number>('countStorage')) + 1);
441          })
442          .width(250).height(60).fontSize(12)
443      }.width(300).height(60)
444
445      Child({ label: 'ChildA' })
446      Child({ label: 'ChildB' })
447
448      Text(`playCount in LocalStorage for debug ${storage.get<number>('countStorage')}`)
449        .width(300).height(60).fontSize(12)
450    }
451  }
452}
453```
454
455
456### Example of Sharing a LocalStorage Instance from UIAbility to One or More Pages
457
458In the preceding examples, the LocalStorage instance is shared only in an \@Entry decorated component and its child component (a page). To enable a LocalStorage instance to be shared across pages, you can create a LocalStorage instance in its owning UIAbility and call windowStage.[loadContent](../reference/apis-arkui/js-apis-window.md#loadcontent9).
459
460
461```ts
462// EntryAbility.ets
463import { UIAbility } from '@kit.AbilityKit';
464import { window } from '@kit.ArkUI';
465
466export default class EntryAbility extends UIAbility {
467  para: Record<string, number> = {
468    'PropA': 47
469  };
470  storage: LocalStorage = new LocalStorage(this.para);
471
472  onWindowStageCreate(windowStage: window.WindowStage) {
473    windowStage.loadContent('pages/Index', this.storage);
474  }
475}
476```
477> **NOTE**
478>
479> On the page, call the **getShared** API to obtain the LocalStorage instance shared through **loadContent**.
480>
481> **LocalStorage.getShared()** works only on emulators and real devices, not in DevEco Studio Previewer.
482
483
484In the following example, **propA** on the **Index** page uses the **getShared()** API to obtain the shared LocalStorage instance. Click the button to go to the **Page** page. Click **Change propA** and then return to the **Index** page. It can be observed that the value of **propA** on the page is changed.
485```ts
486// index.ets
487
488// Use the getShared API to obtain the LocalStorage instance shared by stage.
489@Entry({ storage: LocalStorage.getShared() })
490@Component
491struct Index {
492  // You can use @LocalStorageLink/Prop to establish a relationship with the variables in the LocalStorage instance.
493  @LocalStorageLink('PropA') propA: number = 1;
494  pageStack: NavPathStack = new NavPathStack();
495
496  build() {
497    Navigation(this.pageStack) {
498      Row(){
499        Column() {
500          Text(`${this.propA}`)
501            .fontSize(50)
502            .fontWeight(FontWeight.Bold)
503          Button("To Page")
504            .onClick(() => {
505              this.pageStack.pushPathByName('Page', null);
506            })
507        }
508        .width('100%')
509      }
510      .height('100%')
511    }
512  }
513}
514```
515
516```ts
517// Page.ets
518
519@Builder
520export function PageBuilder() {
521  Page()
522}
523
524// The Page component obtains the LocalStorage instance of the parent component Index.
525@Component
526struct Page {
527  @LocalStorageLink('PropA') propA: number = 2;
528  pathStack: NavPathStack = new NavPathStack();
529
530  build() {
531    NavDestination() {
532      Row(){
533        Column() {
534          Text(`${this.propA}`)
535            .fontSize(50)
536            .fontWeight(FontWeight.Bold)
537
538          Button("Change propA")
539            .onClick(() => {
540              this.propA = 100;
541            })
542
543          Button("Back Index")
544            .onClick(() => {
545              this.pathStack.pop();
546            })
547        }
548        .width('100%')
549      }
550    }
551    .onReady((context: NavDestinationContext) => {
552      this.pathStack = context.pathStack;
553    })
554  }
555}
556```
557When using **Navigation**, you need to add the **route_map.json** file to the **src/main/resources/base/profile** directory, replace the value of **pageSourceFile** with the path of **Page**, and add **"routerMap": "$profile: route_map"** to the **module.json5** file.
558```json
559{
560  "routerMap": [
561    {
562      "name": "Page",
563      "pageSourceFile": "src/main/ets/pages/Page.ets",
564      "buildFunction": "PageBuilder",
565      "data": {
566        "description" : "LocalStorage example"
567      }
568    }
569  ]
570}
571```
572
573> **NOTE**
574>
575> It is good practice to always create a LocalStorage instance with meaningful default values, which serve as a backup when execution exceptions occur and are also useful for unit testing of pages.
576
577
578### Example of Providing a Custom Component with Access to a LocalStorage Instance
579
580LocalStorage instances are accessible to both root nodes through @Entry and custom components (child nodes) through constructors.
581
582This example uses \@LocalStorageLink to implement the following:
583
584- The text in the parent component reads **PropA**, value of **PropA** in the LocalStorage instance **localStorage1**.
585
586- The text in the **Child** component reads **PropB**, value of **PropB** in the LocalStorage instance **localStorage2**.
587
588> **NOTE**
589>
590> LocalStorage instances are accessible to custom components since API version 12.
591> If a custom component functions as a subnode and has member attributes defined, a LocalStorage instance must be passed in as the second parameter. Otherwise, a type mismatch error is reported at compile time.
592> If a custom component has any attribute defined, it does not accept a LocalStorage instance as the only input parameter. If a custom component does not have any attribute defined, it can accept a LocalStorage instance as the only input parameter.
593> If the defined attribute does not need to be initialized from the parent component, {} must be passed in as the first parameter.
594> The LocalStorage instance that is passed to a child component as a constructor parameter is determined at initialization. You can use @LocalStorageLink or the API of LocalStorage to modify the attribute values stored in the LocalStorage instance, but the LocalStorage instance itself cannot be dynamically modified.
595
596```ts
597let localStorage1: LocalStorage = new LocalStorage();
598localStorage1.setOrCreate('PropA', 'PropA');
599
600let localStorage2: LocalStorage = new LocalStorage();
601localStorage2.setOrCreate('PropB', 'PropB');
602
603@Entry(localStorage1)
604@Component
605struct Index {
606  // PropA is in two-way synchronization with PropA in localStorage1.
607  @LocalStorageLink('PropA') PropA: string = 'Hello World';
608  @State count: number = 0;
609
610  build() {
611    Row() {
612      Column() {
613        Text(this.PropA)
614          .fontSize(50)
615          .fontWeight(FontWeight.Bold)
616        // Use the LocalStorage instance localStorage2.
617        Child({ count: this.count }, localStorage2)
618      }
619      .width('100%')
620    }
621    .height('100%')
622  }
623}
624
625
626@Component
627struct Child {
628  @Link count: number;
629  // Hello World is in two-way synchronization with PropB in localStorage2. If there is no PropB in localStorage2, the default value Hello World is used.
630  @LocalStorageLink('PropB') PropB: string = 'Hello World';
631
632  build() {
633    Text(this.PropB)
634      .fontSize(50)
635      .fontWeight(FontWeight.Bold)
636  }
637}
638```
639
6401. If a custom component does not have any attribute defined, it can accept a LocalStorage instance as the only input parameter.
641
642```ts
643let localStorage1: LocalStorage = new LocalStorage();
644localStorage1.setOrCreate('PropA', 'PropA');
645
646let localStorage2: LocalStorage = new LocalStorage();
647localStorage2.setOrCreate('PropB', 'PropB');
648
649@Entry(localStorage1)
650@Component
651struct Index {
652  // PropA is in two-way synchronization with PropA in localStorage1.
653  @LocalStorageLink('PropA') PropA: string = 'Hello World';
654  @State count: number = 0;
655
656  build() {
657    Row() {
658      Column() {
659        Text(this.PropA)
660          .fontSize(50)
661          .fontWeight(FontWeight.Bold)
662        // Use the LocalStorage instance localStorage2.
663        Child(localStorage2)
664      }
665      .width('100%')
666    }
667    .height('100%')
668  }
669}
670
671
672@Component
673struct Child {
674  build() {
675    Text("hello")
676      .fontSize(50)
677      .fontWeight(FontWeight.Bold)
678  }
679}
680```
681
6822. If the defined attribute does not need to be initialized from the parent component, {} must be passed in as the first parameter.
683
684```ts
685let localStorage1: LocalStorage = new LocalStorage();
686localStorage1.setOrCreate('PropA', 'PropA');
687
688let localStorage2: LocalStorage = new LocalStorage();
689localStorage2.setOrCreate('PropB', 'PropB');
690
691@Entry(localStorage1)
692@Component
693struct Index {
694  // PropA is in two-way synchronization with PropA in localStorage1.
695  @LocalStorageLink('PropA') PropA: string = 'Hello World';
696  @State count: number = 0;
697
698  build() {
699    Row() {
700      Column() {
701        Text(this.PropA)
702          .fontSize(50)
703          .fontWeight(FontWeight.Bold)
704        // Use the LocalStorage instance localStorage2.
705        Child({}, localStorage2)
706      }
707      .width('100%')
708    }
709    .height('100%')
710  }
711}
712
713
714@Component
715struct Child {
716  @State count: number = 5;
717  // Hello World is in two-way synchronization with PropB in localStorage2. If there is no PropB in localStorage2, the default value Hello World is used.
718  @LocalStorageLink('PropB') PropB: string = 'Hello World';
719
720  build() {
721    Text(this.PropB)
722      .fontSize(50)
723      .fontWeight(FontWeight.Bold)
724  }
725}
726```
727
728
729### Using LocalStorage with a Navigation Component
730
731You can pass multiple LocalStorage instances to a custom component and bind them to different target navigation pages, which can then display the attribute values of the bound instances.
732
733This example uses \@LocalStorageLink to implement the following:
734
735- Clicking **Next Page** in the parent component creates and redirects to the page named **pageOne**. The text displayed on the page is the value of **PropA** bound to the LocalStorage instance **localStorageA**, that is, **PropA**.
736
737- Clicking **Next Page** on the page creates and redirects to the page named **pageTwo**. The text displayed on the page is the value of **PropB** bound to the LocalStorage instance **localStorageB**, that is, **PropB**.
738
739- Clicking **Next Page** on the page again creates and redirects to the page named **pageTree**. The text displayed on the page is the value of **PropC** bound to the LocalStorage instance **localStorageC**, that is, **PropC**.
740
741- Clicking **Next Page** on the page again creates and redirects to the page named **pageOne**. The text displayed on the page is the value of **PropA** bound to the LocalStorage instance **localStorageA**, that is, **PropA**.
742
743- The **Text** component in the **NavigationContentMsgStack** custom component shares the value of **PropA** bound to the LocalStorage instance in the custom component tree.
744
745
746```ts
747let localStorageA: LocalStorage = new LocalStorage();
748localStorageA.setOrCreate('PropA', 'PropA');
749
750let localStorageB: LocalStorage = new LocalStorage();
751localStorageB.setOrCreate('PropB', 'PropB');
752
753let localStorageC: LocalStorage = new LocalStorage();
754localStorageC.setOrCreate('PropC', 'PropC');
755
756@Entry
757@Component
758struct MyNavigationTestStack {
759  @Provide('pageInfo') pageInfo: NavPathStack = new NavPathStack();
760
761  @Builder
762  PageMap(name: string) {
763    if (name === 'pageOne') {
764      // Pass multiple LocalStorage instances.
765      pageOneStack({}, localStorageA)
766    } else if (name === 'pageTwo') {
767      pageTwoStack({}, localStorageB)
768    } else if (name === 'pageThree') {
769      pageThreeStack({}, localStorageC)
770    }
771  }
772
773  build() {
774    Column({ space: 5 }) {
775      Navigation(this.pageInfo) {
776        Column() {
777          Button('Next Page', { stateEffect: true, type: ButtonType.Capsule })
778            .width('80%')
779            .height(40)
780            .margin(20)
781            .onClick(() => {
782              this.pageInfo.pushPath({ name: 'pageOne' }); // Push the navigation destination page specified by name to the navigation stack.
783            })
784        }
785      }.title('NavIndex')
786      .navDestination(this.PageMap)
787      .mode(NavigationMode.Stack)
788      .borderWidth(1)
789    }
790  }
791}
792
793@Component
794struct pageOneStack {
795  @Consume('pageInfo') pageInfo: NavPathStack;
796  @LocalStorageLink('PropA') PropA: string = 'Hello World';
797
798  build() {
799    NavDestination() {
800      Column() {
801        NavigationContentMsgStack()
802        // Display the value of PropA in the bound LocalStorage instance.
803        Text(`${this.PropA}`)
804        Button('Next Page', { stateEffect: true, type: ButtonType.Capsule })
805          .width('80%')
806          .height(40)
807          .margin(20)
808          .onClick(() => {
809            this.pageInfo.pushPathByName('pageTwo', null);
810          })
811      }.width('100%').height('100%')
812    }.title('pageOne')
813    .onBackPressed(() => {
814      this.pageInfo.pop();
815      return true;
816    })
817  }
818}
819
820@Component
821struct pageTwoStack {
822  @Consume('pageInfo') pageInfo: NavPathStack;
823  @LocalStorageLink('PropB') PropB: string = 'Hello World';
824
825  build() {
826    NavDestination() {
827      Column() {
828        NavigationContentMsgStack()
829        // If there is no PropB in the bound LocalStorage instance, the locally initialized value Hello World is displayed.
830        Text(`${this.PropB}`)
831        Button('Next Page', { stateEffect: true, type: ButtonType.Capsule })
832          .width('80%')
833          .height(40)
834          .margin(20)
835          .onClick(() => {
836            this.pageInfo.pushPathByName('pageThree', null);
837          })
838
839      }.width('100%').height('100%')
840    }.title('pageTwo')
841    .onBackPressed(() => {
842      this.pageInfo.pop();
843      return true;
844    })
845  }
846}
847
848@Component
849struct pageThreeStack {
850  @Consume('pageInfo') pageInfo: NavPathStack;
851  @LocalStorageLink('PropC') PropC: string = 'pageThreeStack';
852
853  build() {
854    NavDestination() {
855      Column() {
856        NavigationContentMsgStack()
857
858        // If there is no PropC in the bound LocalStorage instance, the locally initialized value pageThreeStack is displayed.
859        Text(`${this.PropC}`)
860        Button('Next Page', { stateEffect: true, type: ButtonType.Capsule })
861          .width('80%')
862          .height(40)
863          .margin(20)
864          .onClick(() => {
865            this.pageInfo.pushPathByName('pageOne', null);
866          })
867
868      }.width('100%').height('100%')
869    }.title('pageThree')
870    .onBackPressed(() => {
871      this.pageInfo.pop();
872      return true;
873    })
874  }
875}
876
877@Component
878struct NavigationContentMsgStack {
879  @LocalStorageLink('PropA') PropA: string = 'Hello';
880
881  build() {
882    Column() {
883      Text(`${this.PropA}`)
884        .fontSize(30)
885        .fontWeight(FontWeight.Bold)
886    }
887  }
888}
889```
890
891
892### Union Type
893
894In the following example, the type of variable **A** is **number | null**, and the type of variable **B** is **number | undefined**. The **Text** components display **null** and **undefined** upon initialization, numbers when clicked, and **null** and **undefined** when clicked again.
895
896```ts
897@Component
898struct LocalStorLink {
899  @LocalStorageLink("LinkA") LinkA: number | null = null;
900  @LocalStorageLink("LinkB") LinkB: number | undefined = undefined;
901
902  build() {
903    Column() {
904      Text("@LocalStorageLink initialization, @LocalStorageLink value")
905      Text(this.LinkA + "").fontSize(20).onClick(() => {
906        this.LinkA ? this.LinkA = null : this.LinkA = 1;
907      })
908      Text(this.LinkB + "").fontSize(20).onClick(() => {
909        this.LinkB ? this.LinkB = undefined : this.LinkB = 1;
910      })
911    }
912    .borderWidth(3).borderColor(Color.Green)
913
914  }
915}
916
917@Component
918struct LocalStorProp {
919  @LocalStorageProp("PropA") PropA: number | null = null;
920  @LocalStorageProp("PropB") PropB: number | undefined = undefined;
921
922  build() {
923    Column() {
924      Text("@LocalStorageProp initialization, @LocalStorageProp value")
925      Text(this.PropA + "").fontSize(20).onClick(() => {
926        this.PropA ? this.PropA = null : this.PropA = 1;
927      })
928      Text(this.PropB + "").fontSize(20).onClick(() => {
929        this.PropB ? this.PropB = undefined : this.PropB = 1;
930      })
931    }
932    .borderWidth(3).borderColor(Color.Yellow)
933
934  }
935}
936
937let storage: LocalStorage = new LocalStorage();
938
939@Entry(storage)
940@Component
941struct Index {
942  build() {
943    Row() {
944      Column() {
945        LocalStorLink()
946        LocalStorProp()
947      }
948      .width('100%')
949    }
950    .height('100%')
951  }
952}
953```
954
955
956### Decorating Variables of the Date Type
957
958> **NOTE**
959>
960> LocalStorage supports the Date type since API version 12.
961
962In this example, the **selectedDate** variable decorated by @LocalStorageLink is of the Date type. After the button is clicked, the value of **selectedDate** changes, and the UI is re-rendered.
963
964```ts
965@Entry
966@Component
967struct LocalDateSample {
968  @LocalStorageLink("date") selectedDate: Date = new Date('2021-08-08');
969
970  build() {
971    Column() {
972      Button('set selectedDate to 2023-07-08')
973        .margin(10)
974        .onClick(() => {
975          this.selectedDate = new Date('2023-07-08');
976        })
977      Button('increase the year by 1')
978        .margin(10)
979        .onClick(() => {
980          this.selectedDate.setFullYear(this.selectedDate.getFullYear() + 1);
981        })
982      Button('increase the month by 1')
983        .margin(10)
984        .onClick(() => {
985          this.selectedDate.setMonth(this.selectedDate.getMonth() + 1);
986        })
987      Button('increase the day by 1')
988        .margin(10)
989        .onClick(() => {
990          this.selectedDate.setDate(this.selectedDate.getDate() + 1);
991        })
992      DatePicker({
993        start: new Date('1970-1-1'),
994        end: new Date('2100-1-1'),
995        selected: $$this.selectedDate
996      })
997    }.width('100%')
998  }
999}
1000```
1001
1002
1003### Decorating Variables of the Map Type
1004
1005> **NOTE**
1006>
1007> LocalStorage supports the Map type since API version 12.
1008
1009In this example, the **message** variable decorated by @LocalStorageLink is of the **Map\<number, string\>** type. After the button is clicked, the value of **message** changes, and the UI is re-rendered.
1010
1011```ts
1012@Entry
1013@Component
1014struct LocalMapSample {
1015  @LocalStorageLink("map") message: Map<number, string> = new Map([[0, "a"], [1, "b"], [3, "c"]]);
1016
1017  build() {
1018    Row() {
1019      Column() {
1020        ForEach(Array.from(this.message.entries()), (item: [number, string]) => {
1021          Text(`${item[0]}`).fontSize(30)
1022          Text(`${item[1]}`).fontSize(30)
1023          Divider()
1024        })
1025        Button('init map').onClick(() => {
1026          this.message = new Map([[0, "a"], [1, "b"], [3, "c"]]);
1027        })
1028        Button('set new one').onClick(() => {
1029          this.message.set(4, "d");
1030        })
1031        Button('clear').onClick(() => {
1032          this.message.clear();
1033        })
1034        Button('replace the existing one').onClick(() => {
1035          this.message.set(0, "aa");
1036        })
1037        Button('delete the existing one').onClick(() => {
1038          this.message.delete(0);
1039        })
1040      }
1041      .width('100%')
1042    }
1043    .height('100%')
1044  }
1045}
1046```
1047
1048
1049### Decorating Variables of the Set Type
1050
1051> **NOTE**
1052>
1053> LocalStorage supports the Set type since API version 12.
1054
1055In this example, the **memberSet** variable decorated by @LocalStorageLink is of the **Set\<number\>** type. After the button is clicked, the value of **memberSet** changes, and the UI is re-rendered.
1056
1057```ts
1058@Entry
1059@Component
1060struct LocalSetSample {
1061  @LocalStorageLink("set") memberSet: Set<number> = new Set([0, 1, 2, 3, 4]);
1062
1063  build() {
1064    Row() {
1065      Column() {
1066        ForEach(Array.from(this.memberSet.entries()), (item: [number, string]) => {
1067          Text(`${item[0]}`)
1068            .fontSize(30)
1069          Divider()
1070        })
1071        Button('init set')
1072          .onClick(() => {
1073            this.memberSet = new Set([0, 1, 2, 3, 4]);
1074          })
1075        Button('set new one')
1076          .onClick(() => {
1077            this.memberSet.add(5);
1078          })
1079        Button('clear')
1080          .onClick(() => {
1081            this.memberSet.clear();
1082          })
1083        Button('delete the first one')
1084          .onClick(() => {
1085            this.memberSet.delete(0);
1086          })
1087      }
1088      .width('100%')
1089    }
1090    .height('100%')
1091  }
1092}
1093```
1094
1095### Changing State Variables Outside a Custom Component
1096
1097```ts
1098let storage = new LocalStorage();
1099storage.setOrCreate('count', 47);
1100
1101class Model {
1102  storage: LocalStorage = storage;
1103
1104  call(propName: string, value: number) {
1105    this.storage.setOrCreate<number>(propName, value);
1106  }
1107}
1108
1109let model: Model = new Model();
1110
1111@Entry({ storage: storage })
1112@Component
1113struct Test {
1114  @LocalStorageLink('count') count: number = 0;
1115
1116  build() {
1117    Column() {
1118      Text(`Value of count: ${this.count}`)
1119      Button('change')
1120        .onClick(() => {
1121          model.call('count', this.count + 1);
1122        })
1123    }
1124  }
1125}
1126```
1127
1128<!--no_check-->
1129