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> 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 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 38- Once created, the type of a named attribute cannot be changed. Subsequent calls to **Set** must set a value of same type. 39- LocalStorage provides page-level storage. The [GetShared](../reference/arkui-ts/ts-state-management.md#getshared10) API can only obtain the LocalStorage instance passed through [windowStage.loadContent](../reference/apis/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. Use \@LocalStorageProp(key) or \@LocalStorageLink(key) to decorate variables in the component. **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> Since API version 9, this decorator is supported in ArkTS widgets. 53 54 55By 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 be synchronized to LocalStorage. An update to the attribute with the given key in LocalStorage will overwrite local changes. 56 57 58### Rules of Use 59 60| \@LocalStorageProp Decorator| Description | 61| ----------------------- | ---------------------------------------- | 62| Decorator parameters | **key**: constant string, mandatory (note, the string is quoted) | 63| 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. Whenever possible, use the same type as that of the corresponding attribute in LocalStorage. Otherwise, implicit type conversion occurs, causing application behavior exceptions. **any** is not supported. The **undefined** and **null** values are not allowed.| 64| 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.| 65| Initial value for the decorated variable | Mandatory. It is used as the default value for initialization if the attribute does not exist in LocalStorage.| 66 67 68### Variable Transfer/Access Rules 69 70| Transfer/Access | Description | 71| ---------- | ---------------------------------------- | 72| Initialization and update from the parent component| Forbidden.| 73| Subnode initialization | Supported; can be used to initialize an \@State, \@Link, \@Prop, or \@Provide decorated variable in the child component.| 74| Access | None. | 75 76 **Figure 1** \@LocalStorageProp initialization rule 77 78 79 80 81### Observed Changes and Behavior 82 83**Observed Changes** 84 85 86- When the decorated variable is of the Boolean, string, or number type, its value change can be observed. 87 88- 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. 89 90- When the decorated variable is of the array type, the addition, deletion, and updates of array items can be observed. 91 92 93**Framework Behavior** 94 95 96- 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. 97 98- 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. 99 100- 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. 101 102- 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. 103 104 105## \@LocalStorageLink 106 107\@LocalStorageLink is required if you need to synchronize the changes of the state variables in a custom component back to LocalStorage. 108 109\@LocalStorageLink(key) creates a two-way data synchronization with the attribute with the given key in LocalStorage. 110 1111. If a local change occurs, it is synchronized to LocalStorage. 112 1132. 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). 114 115 116### Rules of Use 117 118| \@LocalStorageLink Decorator| Description | 119| ----------------------- | ---------------------------------------- | 120| Decorator parameters | **key**: constant string, mandatory (note, the string is quoted) | 121| 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. Whenever possible, use the same type as that of the corresponding attribute in LocalStorage. Otherwise, implicit type conversion occurs, causing application behavior exceptions. **any** is not supported. The **undefined** and **null** values are not allowed.| 122| Synchronization type | Two-way: from the attribute in LocalStorage to the custom component variable and back| 123| Initial value for the decorated variable | Mandatory. It is used as the default value for initialization if the attribute does not exist in LocalStorage.| 124 125 126### Variable Transfer/Access Rules 127 128| Transfer/Access | Description | 129| ---------- | ---------------------------------------- | 130| Initialization and update from the parent component| Forbidden.| 131| Subnode initialization | Supported; can be used to initialize an \@State, \@Link, \@Prop, or \@Provide decorated variable in the child component.| 132| Access | None. | 133 134 135 **Figure 2** \@LocalStorageLink initialization rule 136 137 138 139 140 141### Observed Changes and Behavior 142 143**Observed Changes** 144 145 146- When the decorated variable is of the Boolean, string, or number type, its value change can be observed. 147 148- 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. 149 150- When the decorated variable is of the array type, the addition, deletion, and updates of array items can be observed. 151 152 153**Framework Behavior** 154 155 1561. 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. 157 1582. 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. 159 1603. 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. 161 162 163## Application Scenarios 164 165 166### Example of Using LocalStorage in Application Logic 167 168 169```ts 170let storage = new LocalStorage(); // Create a new instance and initialize it with the given object. 171storage['PropA'] = 47 172let propA = storage.get<number>('PropA') // propA == 47 173let link1 = storage.link<number>('PropA'); // link1.get() == 47 174let link2 = storage.link<number>('PropA'); // link2.get() == 47 175let prop = storage.prop<number>('PropA'); // prop.get() = 47 176link1.set(48); // two-way sync: link1.get() == link2.get() == prop.get() == 48 177prop.set(1); // one-way sync: prop.get()=1; but link1.get() == link2.get() == 48 178link1.set(49); // two-way sync: link1.get() == link2.get() == prop.get() == 49 179``` 180 181 182### Example for Using LocalStorage from Inside the UI 183 184The two decorators \@LocalStorageProp and \@LocalStorageLink can work together to obtain the state variable stored in a LocalStorage instance in the UI component. 185 186This example uses \@LocalStorage as an example to show how to: 187 188- Use the **build** function to create a LocalStorage instance named **storage**. 189 190- Use the \@Entry decorator to add **storage** to the top-level component **CompA**. 191 192- Use \@LocalStorageLink to create a two-way data synchronization with the given attribute in LocalStorage. 193 194 ```ts 195 // Create a new instance and initialize it with the given object. 196 let storage = new LocalStorage(); 197 storage['PropA'] = 47; 198 199 @Component 200 struct Child { 201 // @LocalStorageLink creates a two-way data synchronization with the PropA attribute in LocalStorage. 202 @LocalStorageLink('PropA') storLink2: number = 1; 203 204 build() { 205 Button(`Child from LocalStorage ${this.storLink2}`) 206 // The changes will be synchronized to PropA in LocalStorage and with Parent.storLink1. 207 .onClick(() => this.storLink2 += 1) 208 } 209 } 210 // Make LocalStorage accessible from the @Component decorated component. 211 @Entry(storage) 212 @Component 213 struct CompA { 214 // @LocalStorageLink creates a two-way data synchronization with the PropA attribute in LocalStorage. 215 @LocalStorageLink('PropA') storLink1: number = 1; 216 217 build() { 218 Column({ space: 15 }) { 219 Button(`Parent from LocalStorage ${this.storLink1}`) // initial value from LocalStorage will be 47, because 'PropA' initialized already 220 .onClick(() => this.storLink1 += 1) 221 // The @Component decorated child component automatically obtains access to the CompA LocalStorage instance. 222 Child() 223 } 224 } 225 } 226 ``` 227 228 229### Simple Example of Using \@LocalStorageProp with LocalStorage 230 231In this example, the **CompA** and **Child** components create local data that is one-way synced with the PropA attribute in the LocalStorage instance **storage**. 232 233- The change of **this.storProp1** in **CompA** takes effect only in **CompA** and is not synchronized to **storage**. 234 235- In the **Child** component, the value of **storProp2** bound to **Text** is still 47. 236 237 ```ts 238 // Create a new instance and initialize it with the given object. 239 let storage = new LocalStorage(); 240 storage['PropA'] = 47; 241 242 // Make LocalStorage accessible from the @Component decorated component. 243 @Entry(storage) 244 @Component 245 struct CompA { 246 // @LocalStorageProp creates a one-way data synchronization with the PropA attribute in LocalStorage. 247 @LocalStorageProp('PropA') storProp1: number = 1; 248 249 build() { 250 Column({ space: 15 }) { 251 // 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. 252 Button(`Parent from LocalStorage ${this.storProp1}`) 253 .onClick(() => this.storProp1 += 1) 254 Child() 255 } 256 } 257 } 258 259 @Component 260 struct Child { 261 // @LocalStorageProp creates a one-way data synchronization with the PropA attribute in LocalStorage. 262 @LocalStorageProp('PropA') storProp2: number = 2; 263 264 build() { 265 Column({ space: 15 }) { 266 // When CompA changes, the current storProp2 does not change, and 47 is displayed. 267 Text(`Parent from LocalStorage ${this.storProp2}`) 268 } 269 } 270 } 271 ``` 272 273 274### Simple Example of Using \@LocalStorageLink and LocalStorage 275 276This example shows how to create a two-way data synchronization between an \@LocalStorageLink decorated variable and LocalStorage. 277 278 279```ts 280// Create a LocalStorage instance. 281let storage = new LocalStorage(); 282storage['PropA'] = 47; 283// Invoke the link9+ API to create a two-way data synchronization with PropA. linkToPropA is a global variable. 284let linkToPropA = storage.link<number>('PropA'); 285 286@Entry(storage) 287@Component 288struct CompA { 289 290 // @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. 291 @LocalStorageLink('PropA') storLink: number = 1; 292 293 build() { 294 Column() { 295 Text(`incr @LocalStorageLink variable`) 296 // 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. 297 298 .onClick(() => this.storLink += 1) 299 300 // Avoid using the global variable linkToPropA.get() in the component. Doing so may cause errors due to different lifecycles. 301 Text(`@LocalStorageLink: ${this.storLink} - linkToPropA: ${linkToPropA.get()}`) 302 } 303 } 304} 305``` 306 307 308### Example of Syncing State Variable Between Sibling Components 309 310This example shows how to use \@LocalStorageLink to create a two-way synchronization for the state between sibling components. 311 312Check the changes in the **Parent** custom component. 313 3141. 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. 315 3162. 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. 317 3183. The **playCount in LocalStorage for debug ${storage.get<number>('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. 319 320Changes in the **Child** custom component: 321 3221. The update of **playCountLink** is synchronized to LocalStorage, and the parent and sibling child custom components are re-rendered accordingly. 323 324 ```ts 325 class Data { 326 countStorage: number = 0; 327 } 328 let data: Data = { countStorage: 1 } 329 let storage = new LocalStorage(data); 330 331 @Component 332 struct Child { 333 // Name the child component instance. 334 label: string = 'no name'; 335 // Two-way synchronization with countStorage in LocalStorage. 336 @LocalStorageLink('countStorage') playCountLink: number = 0; 337 338 build() { 339 Row() { 340 Text(this.label) 341 .width(50).height(60).fontSize(12) 342 Text(`playCountLink ${this.playCountLink}: inc by 1`) 343 .onClick(() => { 344 this.playCountLink += 1; 345 }) 346 .width(200).height(60).fontSize(12) 347 }.width(300).height(60) 348 } 349 } 350 351 @Entry(storage) 352 @Component 353 struct Parent { 354 @LocalStorageLink('countStorage') playCount: number = 0; 355 356 build() { 357 Column() { 358 Row() { 359 Text('Parent') 360 .width(50).height(60).fontSize(12) 361 Text(`playCount ${this.playCount} dec by 1`) 362 .onClick(() => { 363 this.playCount -= 1; 364 }) 365 .width(250).height(60).fontSize(12) 366 }.width(300).height(60) 367 368 Row() { 369 Text('LocalStorage') 370 .width(50).height(60).fontSize(12) 371 Text(`countStorage ${this.playCount} incr by 1`) 372 .onClick(() => { 373 let countStorage: number | undefined = storage.get<number>('countStorage'); 374 if (countStorage != undefined){ 375 countStorage += 1; 376 storage.set<number>('countStorage', countStorage); 377 } 378 }) 379 .width(250).height(60).fontSize(12) 380 }.width(300).height(60) 381 382 Child({ label: 'ChildA' }) 383 Child({ label: 'ChildB' }) 384 385 Text(`playCount in LocalStorage for debug ${storage.get<number>('countStorage')}`) 386 .width(300).height(60).fontSize(12) 387 } 388 } 389 } 390 ``` 391 392 393### Example of Sharing a LocalStorage Instance from UIAbility to One or More Pages 394 395In 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](../reference/apis/js-apis-window.md#loadcontent9). 396 397 398```ts 399// EntryAbility.ts 400import UIAbility from '@ohos.app.ability.UIAbility'; 401import window from '@ohos.window'; 402 403export default class EntryAbility extends UIAbility { 404 storage: LocalStorage = new LocalStorage(); 405 406 onWindowStageCreate(windowStage: window.WindowStage) { 407 this.storage['PropA'] = 47; 408 windowStage.loadContent('pages/Index', this.storage); 409 } 410} 411``` 412 413On the page, call the **getShared** API to obtain the LocalStorage instance shared through **loadContent**. 414 415 416```ts 417// Use the getShared API to obtain the LocalStorage instance shared by stage. 418let storage = LocalStorage.getShared() 419 420@Entry(storage) 421@Component 422struct CompA { 423 // can access LocalStorage instance using 424 // @LocalStorageLink/Prop decorated variables 425 @LocalStorageLink('PropA') varA: number = 1; 426 427 build() { 428 Column() { 429 Text(`${this.varA}`).fontSize(50) 430 } 431 } 432} 433``` 434 435> **NOTE** 436> 437> 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. 438 439<!--no_check--> 440