• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 positioning of AppStorage in 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
184    let storage = new LocalStorage();
185    storage.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. \@LocalStorageProp and \@LocalStorageLink 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
643    let localStorage1: LocalStorage = new LocalStorage();
644    localStorage1.setOrCreate('PropA', 'PropA');
645
646    let localStorage2: LocalStorage = new LocalStorage();
647    localStorage2.setOrCreate('PropB', 'PropB');
648
649    @Entry(localStorage1)
650    @Component
651    struct 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
673    @Component
674    struct Child {
675      build() {
676        Text("hello")
677          .fontSize(50)
678          .fontWeight(FontWeight.Bold)
679      }
680    }
681    ```
682
6832. If the defined attribute does not need to be initialized from the parent component, {} must be passed in as the first parameter.
684
685    ```ts
686    let localStorage1: LocalStorage = new LocalStorage();
687    localStorage1.setOrCreate('PropA', 'PropA');
688
689    let localStorage2: LocalStorage = new LocalStorage();
690    localStorage2.setOrCreate('PropB', 'PropB');
691
692    @Entry(localStorage1)
693    @Component
694    struct Index {
695      // PropA is in two-way synchronization with PropA in localStorage1.
696      @LocalStorageLink('PropA') PropA: string = 'Hello World';
697      @State count: number = 0;
698
699      build() {
700        Row() {
701          Column() {
702            Text(this.PropA)
703              .fontSize(50)
704              .fontWeight(FontWeight.Bold)
705            // Use the LocalStorage instance localStorage2.
706            Child({}, localStorage2)
707          }
708          .width('100%')
709        }
710        .height('100%')
711      }
712    }
713    ```
714
715
716    @Component
717    struct Child {
718      @State count: number = 5;
719      // 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.
720      @LocalStorageLink('PropB') PropB: string = 'Hello World';
721
722      build() {
723        Text(this.PropB)
724          .fontSize(50)
725          .fontWeight(FontWeight.Bold)
726      }
727    }
728    ```
729
730
731### Using LocalStorage with a Navigation Component
732
733You 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.
734
735This example uses \@LocalStorageLink to implement the following:
736
737- 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**.
738
739- 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**.
740
741- 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**.
742
743- 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**.
744
745- The **Text** component in the **NavigationContentMsgStack** custom component shares the value of **PropA** bound to the LocalStorage instance in the custom component tree.
746
747
748```ts
749let localStorageA: LocalStorage = new LocalStorage();
750localStorageA.setOrCreate('PropA', 'PropA');
751
752let localStorageB: LocalStorage = new LocalStorage();
753localStorageB.setOrCreate('PropB', 'PropB');
754
755let localStorageC: LocalStorage = new LocalStorage();
756localStorageC.setOrCreate('PropC', 'PropC');
757
758@Entry
759@Component
760struct MyNavigationTestStack {
761  @Provide('pageInfo') pageInfo: NavPathStack = new NavPathStack();
762
763  @Builder
764  PageMap(name: string) {
765    if (name === 'pageOne') {
766      // Pass multiple LocalStorage instances.
767      pageOneStack({}, localStorageA)
768    } else if (name === 'pageTwo') {
769      pageTwoStack({}, localStorageB)
770    } else if (name === 'pageThree') {
771      pageThreeStack({}, localStorageC)
772    }
773  }
774
775  build() {
776    Column({ space: 5 }) {
777      Navigation(this.pageInfo) {
778        Column() {
779          Button('Next Page', { stateEffect: true, type: ButtonType.Capsule })
780            .width('80%')
781            .height(40)
782            .margin(20)
783            .onClick(() => {
784              this.pageInfo.pushPath({ name: 'pageOne' }); // Push the navigation destination page specified by name to the navigation stack.
785            })
786        }
787      }.title('NavIndex')
788      .navDestination(this.PageMap)
789      .mode(NavigationMode.Stack)
790      .borderWidth(1)
791    }
792  }
793}
794
795@Component
796struct pageOneStack {
797  @Consume('pageInfo') pageInfo: NavPathStack;
798  @LocalStorageLink('PropA') PropA: string = 'Hello World';
799
800  build() {
801    NavDestination() {
802      Column() {
803        NavigationContentMsgStack()
804        // Display the value of PropA in the bound LocalStorage instance.
805        Text(`${this.PropA}`)
806        Button('Next Page', { stateEffect: true, type: ButtonType.Capsule })
807          .width('80%')
808          .height(40)
809          .margin(20)
810          .onClick(() => {
811            this.pageInfo.pushPathByName('pageTwo', null);
812          })
813      }.width('100%').height('100%')
814    }.title('pageOne')
815    .onBackPressed(() => {
816      this.pageInfo.pop();
817      return true;
818    })
819  }
820}
821
822@Component
823struct pageTwoStack {
824  @Consume('pageInfo') pageInfo: NavPathStack;
825  @LocalStorageLink('PropB') PropB: string = 'Hello World';
826
827  build() {
828    NavDestination() {
829      Column() {
830        NavigationContentMsgStack()
831        // If there is no PropB in the bound LocalStorage instance, the locally initialized value Hello World is displayed.
832        Text(`${this.PropB}`)
833        Button('Next Page', { stateEffect: true, type: ButtonType.Capsule })
834          .width('80%')
835          .height(40)
836          .margin(20)
837          .onClick(() => {
838            this.pageInfo.pushPathByName('pageThree', null);
839          })
840
841      }.width('100%').height('100%')
842    }.title('pageTwo')
843    .onBackPressed(() => {
844      this.pageInfo.pop();
845      return true;
846    })
847  }
848}
849
850@Component
851struct pageThreeStack {
852  @Consume('pageInfo') pageInfo: NavPathStack;
853  @LocalStorageLink('PropC') PropC: string = 'pageThreeStack';
854
855  build() {
856    NavDestination() {
857      Column() {
858        NavigationContentMsgStack()
859
860        // If there is no PropC in the bound LocalStorage instance, the locally initialized value pageThreeStack is displayed.
861        Text(`${this.PropC}`)
862        Button('Next Page', { stateEffect: true, type: ButtonType.Capsule })
863          .width('80%')
864          .height(40)
865          .margin(20)
866          .onClick(() => {
867            this.pageInfo.pushPathByName('pageOne', null);
868          })
869
870      }.width('100%').height('100%')
871    }.title('pageThree')
872    .onBackPressed(() => {
873      this.pageInfo.pop();
874      return true;
875    })
876  }
877}
878
879@Component
880struct NavigationContentMsgStack {
881  @LocalStorageLink('PropA') PropA: string = 'Hello';
882
883  build() {
884    Column() {
885      Text(`${this.PropA}`)
886        .fontSize(30)
887        .fontWeight(FontWeight.Bold)
888    }
889  }
890}
891```
892
893
894### Union Type
895
896In 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.
897
898```ts
899@Component
900struct LocalStorLink {
901  @LocalStorageLink("LinkA") LinkA: number | null = null;
902  @LocalStorageLink("LinkB") LinkB: number | undefined = undefined;
903
904  build() {
905    Column() {
906      Text("@LocalStorageLink initialization, @LocalStorageLink value")
907      Text(this.LinkA + "").fontSize(20).onClick(() => {
908        this.LinkA ? this.LinkA = null : this.LinkA = 1;
909      })
910      Text(this.LinkB + "").fontSize(20).onClick(() => {
911        this.LinkB ? this.LinkB = undefined : this.LinkB = 1;
912      })
913    }
914    .borderWidth(3).borderColor(Color.Green)
915
916  }
917}
918
919@Component
920struct LocalStorProp {
921  @LocalStorageProp("PropA") PropA: number | null = null;
922  @LocalStorageProp("PropB") PropB: number | undefined = undefined;
923
924  build() {
925    Column() {
926      Text("@LocalStorageProp initialization, @LocalStorageProp value")
927      Text(this.PropA + "").fontSize(20).onClick(() => {
928        this.PropA ? this.PropA = null : this.PropA = 1;
929      })
930      Text(this.PropB + "").fontSize(20).onClick(() => {
931        this.PropB ? this.PropB = undefined : this.PropB = 1;
932      })
933    }
934    .borderWidth(3).borderColor(Color.Yellow)
935
936  }
937}
938
939let storage: LocalStorage = new LocalStorage();
940
941@Entry(storage)
942@Component
943struct Index {
944  build() {
945    Row() {
946      Column() {
947        LocalStorLink()
948        LocalStorProp()
949      }
950      .width('100%')
951    }
952    .height('100%')
953  }
954}
955```
956
957
958### Decorating Variables of the Date Type
959
960> **NOTE**
961>
962> LocalStorage supports the Date type since API version 12.
963
964In 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.
965
966```ts
967@Entry
968@Component
969struct LocalDateSample {
970  @LocalStorageLink("date") selectedDate: Date = new Date('2021-08-08');
971
972  build() {
973    Column() {
974      Button('set selectedDate to 2023-07-08')
975        .margin(10)
976        .onClick(() => {
977          this.selectedDate = new Date('2023-07-08');
978        })
979      Button('increase the year by 1')
980        .margin(10)
981        .onClick(() => {
982          this.selectedDate.setFullYear(this.selectedDate.getFullYear() + 1);
983        })
984      Button('increase the month by 1')
985        .margin(10)
986        .onClick(() => {
987          this.selectedDate.setMonth(this.selectedDate.getMonth() + 1);
988        })
989      Button('increase the day by 1')
990        .margin(10)
991        .onClick(() => {
992          this.selectedDate.setDate(this.selectedDate.getDate() + 1);
993        })
994      DatePicker({
995        start: new Date('1970-1-1'),
996        end: new Date('2100-1-1'),
997        selected: $$this.selectedDate
998      })
999    }.width('100%')
1000  }
1001}
1002```
1003
1004
1005### Decorating Variables of the Map Type
1006
1007> **NOTE**
1008>
1009> LocalStorage supports the Map type since API version 12.
1010
1011In 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.
1012
1013```ts
1014@Entry
1015@Component
1016struct LocalMapSample {
1017  @LocalStorageLink("map") message: Map<number, string> = new Map([[0, "a"], [1, "b"], [3, "c"]]);
1018
1019  build() {
1020    Row() {
1021      Column() {
1022        ForEach(Array.from(this.message.entries()), (item: [number, string]) => {
1023          Text(`${item[0]}`).fontSize(30)
1024          Text(`${item[1]}`).fontSize(30)
1025          Divider()
1026        })
1027        Button('init map').onClick(() => {
1028          this.message = new Map([[0, "a"], [1, "b"], [3, "c"]]);
1029        })
1030        Button('set new one').onClick(() => {
1031          this.message.set(4, "d");
1032        })
1033        Button('clear').onClick(() => {
1034          this.message.clear();
1035        })
1036        Button('replace the existing one').onClick(() => {
1037          this.message.set(0, "aa");
1038        })
1039        Button('delete the existing one').onClick(() => {
1040          this.message.delete(0);
1041        })
1042      }
1043      .width('100%')
1044    }
1045    .height('100%')
1046  }
1047}
1048```
1049
1050
1051### Decorating Variables of the Set Type
1052
1053> **NOTE**
1054>
1055> LocalStorage supports the Set type since API version 12.
1056
1057In 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.
1058
1059```ts
1060@Entry
1061@Component
1062struct LocalSetSample {
1063  @LocalStorageLink("set") memberSet: Set<number> = new Set([0, 1, 2, 3, 4]);
1064
1065  build() {
1066    Row() {
1067      Column() {
1068        ForEach(Array.from(this.memberSet.entries()), (item: [number, string]) => {
1069          Text(`${item[0]}`)
1070            .fontSize(30)
1071          Divider()
1072        })
1073        Button('init set')
1074          .onClick(() => {
1075            this.memberSet = new Set([0, 1, 2, 3, 4]);
1076          })
1077        Button('set new one')
1078          .onClick(() => {
1079            this.memberSet.add(5);
1080          })
1081        Button('clear')
1082          .onClick(() => {
1083            this.memberSet.clear();
1084          })
1085        Button('delete the first one')
1086          .onClick(() => {
1087            this.memberSet.delete(0);
1088          })
1089      }
1090      .width('100%')
1091    }
1092    .height('100%')
1093  }
1094}
1095```
1096
1097### Changing State Variables Outside a Custom Component
1098
1099```ts
1100let storage = new LocalStorage();
1101storage.setOrCreate('count', 47);
1102
1103class Model {
1104  storage: LocalStorage = storage;
1105
1106  call(propName: string, value: number) {
1107    this.storage.setOrCreate<number>(propName, value);
1108  }
1109}
1110
1111let model: Model = new Model();
1112
1113@Entry({ storage: storage })
1114@Component
1115struct Test {
1116  @LocalStorageLink('count') count: number = 0;
1117
1118  build() {
1119    Column() {
1120      Text(`Value of count: ${this.count}`)
1121      Button('change')
1122        .onClick(() => {
1123          model.call('count', this.count + 1);
1124        })
1125    }
1126  }
1127}
1128```
1129
1130
1131