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