• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# LocalStorage: UI State Storage
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 within a UIAbility.
5
6
7This topic describes only the LocalStorage application scenarios and related decorators: \@LocalStorageProp and \@LocalStorageLink.
8
9
10> **NOTE**
11>
12> This module is supported since API version 9.
13
14
15## Overview
16
17LocalStorage is an in-memory "database" that ArkTS provides for storing state variables that are 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 from the UIAbility, across pages in a UIAbility.
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): \@LocalStorageProp creates a one-way data synchronization from the named attribute in LocalStorage to the \@LocalStorageProp decorated variable.
32
33- [@LocalStorageLink](#localstoragelink): \@LocalStorageLink creates a two-way data synchronization with the named attribute in the \@Component's LocalStorage.
34
35
36## Restrictions
37
38Once created, the type of a named attribute cannot be changed. Subsequent calls to **Set** must set a value of same type.
39
40
41## \@LocalStorageProp
42
43As mentioned above, if you want to establish a binding between LocalStorage and a custom component, you need to use the \@LocalStorageProp and \@LocalStorageLink decorators. Use \@LocalStorageProp(key) or \@LocalStorageLink(key) to decorate variables in the component. **key** identifies the attribute in LocalStorage.
44
45
46When 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.)
47
48
49> **NOTE**
50>
51> Since API version 9, this decorator is supported in ArkTS widgets.
52
53
54By decorating a variable with \@LocalStorageProp(key), a one-way data synchronization is established with the attribute with the given key in LocalStorage. A local change can be made, but it will not besynchronized to LocalStorage. An update to the attribute with the given key in LocalStorage will overwrite local changes.
55
56
57### Rules of Use
58
59| \@LocalStorageProp Decorator| Description                                      |
60| ----------------------- | ---------------------------------------- |
61| Decorator parameters                  | **key**: constant string, mandatory (note, the string is quoted)                 |
62| Allowed variable types              | Object, class, string, number, Boolean, enum, and array of these types. For details about the scenarios of nested objects, see [Observed Changes and Behavior](#observed-changes-and-behavior).<br>The type must be specified and must be the same as the corresponding attribute in LocalStorage. **any** is not supported. The **undefined** and **null** values are not allowed.|
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. It is used as the default value for initialization if the attribute does not exist in LocalStorage.|
65
66
67### Variable Transfer/Access Rules
68
69| Transfer/Access     | Description                                      |
70| ---------- | ---------------------------------------- |
71| Initialization and update from the parent component| Forbidden.|
72| Subnode initialization    | Supported; can be used to initialize an \@State, \@Link, \@Prop, or \@Provide decorated variable in the child component.|
73| Access | None.                                      |
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 and value changes of all its attributes, that is, the attributes that **Object.keys(observedObject)** returns.
88
89- When the decorated variable is of the array type, the addition, deletion, and updates of array items can be observed.
90
91
92**Framework Behavior**
93
94
95- When the value change of the \@LocalStorageProp(key) decorated variable is observed, the change is not synchronized to the attribute with the give key value in LocalStorage.
96
97- The value change of the \@LocalStorageProp(key) decorated variable only applies to the private member variables of the current component, but not other variables bound to the key.
98
99- When the data decorated by \@LocalStorageProp(key) is a state variable, the change of the data is not synchronized to LocalStorage, but the owning custom component is re-rendered.
100
101- When the attribute with the given key in LocalStorage is updated, the change is synchronized to all the \@LocalStorageProp(key) decorated data, and the local changes of the data are overwritten.
102
103
104## \@LocalStorageLink
105
106\@LocalStorageLink is required if you need to synchronize the changes of the state variables in a custom component back to LocalStorage.
107
108\@LocalStorageLink(key) creates a two-way data synchronization with the attribute with the given key in LocalStorage.
109
1101. If a local change occurs, it is synchronized to LocalStorage.
111
1122. 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).
113
114
115### Rules of Use
116
117| \@LocalStorageLink Decorator| Description                                      |
118| ----------------------- | ---------------------------------------- |
119| Decorator parameters                  | **key**: constant string, mandatory (note, the string is quoted)                 |
120| Allowed variable types              | Object, class, string, number, Boolean, enum, and array of these types. For details about the scenarios of nested objects, see [Observed Changes and Behavior](#observed-changes-and-behavior).<br>The type must be specified and must be the same as the corresponding attribute in LocalStorage. **any** is not supported. The **undefined** and **null** values are not allowed.|
121| Synchronization type                   | Two-way: from the attribute in LocalStorage to the custom component variable and back|
122| Initial value for the decorated variable              | Mandatory. It is used as the default value for initialization if the attribute does not exist in LocalStorage.|
123
124
125### Variable Transfer/Access Rules
126
127| Transfer/Access     | Description                                      |
128| ---------- | ---------------------------------------- |
129| Initialization and update from the parent component| Forbidden.|
130| Subnode initialization    | Supported; can be used to initialize a n \@State, \@Link, \@Prop, or \@Provide decorated variable in the child component.|
131| Access | None.                                      |
132
133
134  **Figure 2** \@LocalStorageLink initialization rule
135
136
137![en-us_image_0000001552855957](figures/en-us_image_0000001552855957.png)
138
139
140### Observed Changes and Behavior
141
142**Observed Changes**
143
144
145- When the decorated variable is of the Boolean, string, or number type, its value change can be observed.
146
147- When the decorated variable is of the class or Object type, its value change and value changes of all its attributes, that is, the attributes that **Object.keys(observedObject)** returns.
148
149- When the decorated variable is of the array type, the addition, deletion, and updates of array items can be observed.
150
151
152**Framework Behavior**
153
154
1551. 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.
156
1572. 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.
158
1593. When the data decorated by \@LocalStorageProp(key) is a state variable, the change of the data is synchronized to LocalStorage, and the owning custom component is re-rendered.
160
161
162## Application Scenarios
163
164
165### Example of Using LocalStorage in Application Logic
166
167
168```ts
169let storage = new LocalStorage({ 'PropA': 47 }); // Create a new instance and initialize it with the given object.
170let propA = storage.get('PropA') // propA == 47
171let link1 = storage.link('PropA'); // link1.get() == 47
172let link2 = storage.link('PropA'); // link2.get() == 47
173let prop = storage.prop('PropA'); // prop.get() = 47
174link1.set(48); // two-way sync: link1.get() == link2.get() == prop.get() == 48
175prop.set(1); // one-way sync: prop.get()=1; but link1.get() == link2.get() == 48
176link1.set(49); // two-way sync: link1.get() == link2.get() == prop.get() == 49
177```
178
179
180### Example for Using LocalStorage from Inside the UI
181
182The two decorators \@LocalStorageProp and \@LocalStorageLink can work together to obtain the state variable stored in a LocalStorage instance in the UI component.
183
184This example uses \@LocalStorage as an example to show how to:
185
186- Use the **build** function to create a LocalStorage instance named **storage**.
187
188- Use the \@Entry decorator to add **storage** to the top-level component **CompA**.
189
190- Use \@LocalStorageLink to create a two-way data synchronization with the given attribute in LocalStorage.
191
192  ```ts
193  // Create a new instance and initialize it with the given object.
194  let storage = new LocalStorage({ 'PropA': 47 });
195
196  @Component
197  struct Child {
198    // @LocalStorageLink creates a two-way data synchronization with the ProA attribute in LocalStorage.
199    @LocalStorageLink('PropA') storLink2: number = 1;
200
201    build() {
202      Button(`Child from LocalStorage ${this.storLink2}`)
203        // The changes will be synchronized to ProA in LocalStorage and with Parent.storLink1.
204        .onClick(() => this.storLink2 += 1)
205    }
206  }
207  // Make LocalStorage accessible from the @Component decorated component.
208  @Entry(storage)
209  @Component
210  struct CompA {
211    // @LocalStorageLink creates a two-way data synchronization with the ProA attribute in LocalStorage.
212    @LocalStorageLink('PropA') storLink1: number = 1;
213
214    build() {
215      Column({ space: 15 }) {
216        Button(`Parent from LocalStorage ${this.storLink1}`) // initial value from LocalStorage will be 47, because 'PropA' initialized already
217          .onClick(() => this.storLink1 += 1)
218        // The @Component decorated child component automatically obtains access to the CompA LocalStorage instance.
219        Child()
220      }
221    }
222  }
223  ```
224
225
226### Simple Example of Using \@LocalStorageProp with LocalStorage
227
228In this example, the **CompA** and **Child** components create local data that is one-way synced with the PropA attribute in the LocalStorage instance **storage**.
229
230- The change of **this.storProp1** in **CompA** takes effect only in **CompA** and is not synchronized to **storage**.
231
232- In the **Child** component, the value of **storProp2** bound to **Text** is still 47.
233
234  ```ts
235  // Create a new instance and initialize it with the given object.
236  let storage = new LocalStorage({ 'PropA': 47 });
237  // Make LocalStorage accessible from the @Component decorated component.
238  @Entry(storage)
239  @Component
240  struct CompA {
241    // @LocalStorageProp creates a one-way data synchronization with the ProA attribute in LocalStorage.
242    @LocalStorageProp('PropA') storProp1: number = 1;
243
244    build() {
245      Column({ space: 15 }) {
246        // The initial value is 47. After the button is clicked, the value is incremented by 1. The change takes effect only in storProp1 in the current component and is not synchronized to LocalStorage.
247        Button(`Parent from LocalStorage ${this.storProp1}`)
248          .onClick(() => this.storProp1 += 1)
249        Child()
250      }
251    }
252  }
253
254  @Component
255  struct Child {
256    // @LocalStorageProp creates a one-way data synchronization with the ProA attribute in LocalStorage.
257    @LocalStorageProp('PropA') storProp2: number = 2;
258
259    build() {
260      Column({ space: 15 }) {
261        // When CompA changes, the current storProp2 does not change, and 47 is displayed.
262        Text(`Parent from LocalStorage ${this.storProp2}`)
263      }
264    }
265  }
266  ```
267
268
269### Simple Example of Using \@LocalStorageLink and LocalStorage
270
271This example shows how to create a two-way data synchronization between an \@LocalStorageLink decorated variable and LocalStorage.
272
273
274```ts
275// Create a LocalStorage instance.
276let storage = new LocalStorage({ 'PropA': 47 });
277// Invoke the link9+ API to create a two-way data synchronization with PropA. linkToPropA is a global variable.
278let linkToPropA = storage.link('PropA');
279
280@Entry(storage)
281@Component
282struct CompA {
283
284  // @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.
285  @LocalStorageLink('PropA') storLink: number = 1;
286
287  build() {
288    Column() {
289      Text(`incr @LocalStorageLink variable`)
290        // Clicking incr @LocalStorageLink variable increases the value of this.storLink by 1. The change is synchronized back to the storage. The global variable linkToPropA also changes.
291
292        .onClick(() => this.storLink += 1)
293
294      // You are not advised to use the global variable linkToPropA.get() in the component because errors may occur due to different life cycles.
295      Text(`@LocalStorageLink: ${this.storLink} - linkToPropA: ${linkToPropA.get()}`)
296    }
297  }
298}
299```
300
301
302### State Variable Synchronization Between Sibling Nodes
303
304This example shows how to use \@LocalStorageLink to create a two-way synchronization for the state between sibling nodes.
305
306Check the changes in the **Parent** custom component.
307
3081. Clicking **countStorage ${this.playCount} incr 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.
309
3102. 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.
311
3123. 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.
313
314Changes in the **Child** custom component:
315
3161. The update of **playCountLink** is synchronized to LocalStorage, and the parent and sibling child custom components are re-rendered accordingly.
317
318   ```ts
319   let storage = new LocalStorage({ countStorage: 1 });
320
321   @Component
322   struct Child {
323     // Name the child component instance.
324     label: string = 'no name';
325     // Two-way synchronization with countStorage in LocalStorage.
326     @LocalStorageLink('countStorage') playCountLink: number = 0;
327
328     build() {
329       Row() {
330         Text(this.label)
331           .width(50).height(60).fontSize(12)
332         Text(`playCountLink ${this.playCountLink}: inc by 1`)
333           .onClick(() => {
334             this.playCountLink += 1;
335           })
336           .width(200).height(60).fontSize(12)
337       }.width(300).height(60)
338     }
339   }
340
341   @Entry(storage)
342   @Component
343   struct Parent {
344     @LocalStorageLink('countStorage') playCount: number = 0;
345
346     build() {
347       Column() {
348         Row() {
349           Text('Parent')
350             .width(50).height(60).fontSize(12)
351           Text(`playCount ${this.playCount} dec by 1`)
352             .onClick(() => {
353               this.playCount -= 1;
354             })
355             .width(250).height(60).fontSize(12)
356         }.width(300).height(60)
357
358         Row() {
359           Text('LocalStorage')
360             .width(50).height(60).fontSize(12)
361           Text(`countStorage ${this.playCount} incr by 1`)
362             .onClick(() => {
363               storage.set<number>('countStorage', 1 + storage.get<number>('countStorage'));
364             })
365             .width(250).height(60).fontSize(12)
366         }.width(300).height(60)
367
368         Child({ label: 'ChildA' })
369         Child({ label: 'ChildB' })
370
371         Text(`playCount in LocalStorage for debug ${storage.get<number>('countStorage')}`)
372           .width(300).height(60).fontSize(12)
373       }
374     }
375   }
376   ```
377
378
379### Sharing a LocalStorage Instance from UIAbility to One or More Pages
380
381In the preceding examples, the LocalStorage instance is shared only in an \@Entry decorated component and its owning child component (a page). To enable a LocalStorage instance to be shared across pages, you can create a LocalStorage instance in the owning UIAbility and call windowStage.[loadContent](https://gitee.com/openharmony/docs/blob/master/en/application-dev/reference/apis/js-apis-window.md#loadcontent9).
382
383
384```ts
385// EntryAbility.ts
386import UIAbility from '@ohos.app.ability.UIAbility';
387import window from '@ohos.window';
388
389export default class EntryAbility extends UIAbility {
390  storage: LocalStorage = new LocalStorage({
391    'PropA': 47
392  });
393
394  onWindowStageCreate(windowStage: window.WindowStage) {
395    windowStage.loadContent('pages/Index', this.storage);
396  }
397}
398```
399
400On the page, call the **GetShared** API to obtain the LocalStorage instance shared through **loadContent**.
401
402
403```ts
404// Use the GetShared API to obtain the Storage instance shared by stage.
405let storage = LocalStorage.GetShared()
406
407@Entry(storage)
408@Component
409struct CompA {
410  // can access LocalStorage instance using
411  // @LocalStorageLink/Prop decorated variables
412  @LocalStorageLink('PropA') varA: number = 1;
413
414  build() {
415    Column() {
416      Text(`${this.varA}`).fontSize(50)
417    }
418  }
419}
420```
421
422
423> **NOTE**
424>
425> 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.
426