1# AppStorageV2: Storing Application-wide UI State 2<!--Kit: ArkUI--> 3<!--Subsystem: ArkUI--> 4<!--Owner: @zzq212050299--> 5<!--Designer: @s10021109--> 6<!--Tester: @TerryTsao--> 7<!--Adviser: @zhang_yixin13--> 8 9To enhance the capability of the state management framework to store global UI status variables of applications, you are advised to use AppStorageV2. 10 11AppStorageV2 provides the capability of globally sharing state variables within an application. You can bind the same key through **connect** to share data across abilities. 12 13Before reading this topic, you are advised to read [\@ComponentV2](./arkts-new-componentV2.md), [\@ObservedV2 and \@Trace](./arkts-new-observedV2-and-trace.md), and [AppStorageV2 API reference](../../reference/apis-arkui/js-apis-StateManagement.md#appstoragev2). 14 15>**NOTE** 16> 17>AppStorageV2 is supported since API version 12. 18> 19 20## Overview 21 22AppStorageV2 is a singleton created when the application UI is started. It provides central storage for application state data that is accessible at the application level and remains persistent throughout the application lifecycle. Properties in AppStorageV2 are accessed using unique key strings. It should be noted that data between AppStorage and AppStorageV2 is not shared. 23 24The **connect** API of AppStorageV2 enables customizable synchronization between stored data and UI components. 25 26AppStorageV2 supports state sharing among multiple UIAbility instances in the [main thread](../../application-models/thread-model-stage.md) of the same application. 27 28## How to Use 29 30### connect: Creating or Obtaining Stored Data 31 32```JavaScript 33static connect<T extends object>( 34 type: TypeConstructorWithArgs<T>, 35 keyOrDefaultCreator?: string | StorageDefaultCreator<T>, 36 defaultCreator?: StorageDefaultCreator<T> 37): T | undefined; 38``` 39 40| connect | Description | 41| ------------ | ----------------------------------------------------- | 42| Parameters | **type**: specified type. If no **key** is specified, the name of the **type** is used as the **key**.<br>**keyOrDefaultCreator**: specified key or default constructor.<br>**defaultCreator**: default constructor. | 43| Return value | Returns the data if creation or acquisition is successful; otherwise, returns **undefined**.| 44 45>**NOTE** 46> 47>1. The second parameter is used when no **key** is specified, and the third parameter is used otherwise (including when the second parameter is invalid). 48> 49>2. If the data has been stored in AppStorageV2, you can obtain the stored data without using the default constructor. If the data has not been stored, you must specify a default constructor; otherwise, an application exception will be thrown. 50> 51>3. Ensure that the data types match the key. Connecting different types of data to the same key will result in an application exception. 52> 53>4. You are advised to use meaningful values for keys. The values can contain letters, digits, and underscores (_) and a maximum of 255 characters. Using invalid characters or null characters will result in undefined behavior. 54> 55>5. When matching the key with the [\@Observed](arkts-observed-and-objectlink.md) object, specify the key or customize the **name** property. 56 57### remove: Deleting the Stored Data of a Specified Key 58 59```JavaScript 60static remove<T>(keyOrType: string | TypeConstructorWithArgs<T>): void; 61``` 62 63| remove | Description | 64| ------------ | ----------------------------------------------------- | 65| Parameters | **keyOrType**: key to be deleted. If the key is of the **Type**, the key to be deleted is the name of the **Type**. | 66| Return value | N/A| 67 68>**NOTE** 69> 70>If a key that does not exist in AppStorageV2 is deleted, a warning is reported. 71 72### keys: Returning All Keys Stored in AppStorageV2 73 74```JavaScript 75static keys(): Array<string>; 76``` 77 78| keys | Description | 79| ------------ | ----------------------------------------------------- | 80| Parameters | N/A | 81| Return value | All keys stored in AppStorageV2.| 82 83 84## Constraints 85 861. This singleton must be used together with the UI thread only. Other threads, for example, @Sendable decorator is not supported. 87 882. Types such as **collections.Set** and **collections.Map** are not supported. 89 903. Non-built-in types, such as PixelMap, NativePointer, ArrayList, and other native types, are not supported. 91 924. Primitive types, such as string, number, and boolean, are not supported. 93 94## Use Scenarios 95 96### Using AppStorageV2 97 98AppStorageV2 provides the **connect** API to enable data modification and synchronization. When modified data is decorated with @Trace, changes automatically trigger UI re-rendering. Note that the **remove** API only deletes data from AppStorageV2 without affecting already instantiated component data. 99 100```ts 101import { AppStorageV2 } from '@kit.ArkUI'; 102 103@ObservedV2 104class Message { 105 @Trace userID: number; 106 userName: string; 107 108 constructor(userID?: number, userName?: string) { 109 this.userID = userID ?? 1; 110 this.userName = userName ?? 'Jack'; 111 } 112} 113 114@Entry 115@ComponentV2 116struct Index { 117 // Use connect to create an object with key Message in AppStorageV2. 118 // Changes to the return value of connect will be synchronized back to AppStorageV2. 119 @Local message: Message = AppStorageV2.connect<Message>(Message, () => new Message())!; 120 121 build() { 122 Column() { 123 // Modifying class properties decorated with @Trace will synchronously update the UI. 124 Button(`Index userID: ${this.message.userID}`) 125 .onClick(() => { 126 this.message.userID += 1; 127 }) 128 // Modifying class properties not decorated with @Trace will not update the UI, but changes are still synchronized back to AppStorageV2. 129 Button(`Index userName: ${this.message.userName}`) 130 .onClick(() => { 131 this.message.userName += 'suf'; 132 }) 133 // Clicking the button deletes the object with key Message from AppStorageV2. 134 // After removal, changes to the parent component's userId are still synchronized to the child component, because remove only deletes the object from AppStorageV2 and does not affect the existing component data. 135 Button('remove key: Message') 136 .onClick(() => { 137 AppStorageV2.remove<Message>(Message); 138 }) 139 // Clicking the button adds an object with key Message to AppStorageV2. 140 // After removal, when the key is re-added and the userID in both parent and child components is modified, it is found that the data is out of sync. Once the child component reconnects via connect(), the data becomes consistent again. 141 Button('connect key: Message') 142 .onClick(() => { 143 this.message = AppStorageV2.connect<Message>(Message, () => new Message(5, 'Rose'))!; 144 }) 145 Divider() 146 Child() 147 } 148 .width('100%') 149 .height('100%') 150 } 151} 152 153@ComponentV2 154struct Child { 155 // Use connect to obtain the object with key Message from AppStorageV2 (created in the parent component). 156 @Local message: Message = AppStorageV2.connect<Message>(Message, () => new Message())!; 157 @Local name: string = this.message.userName; 158 159 build() { 160 Column() { 161 // Modifying @Trace decorated properties updates the UI, and the change is propagated to the parent component. 162 Button(`Child userID: ${this.message.userID}`) 163 .onClick(() => { 164 this.message.userID += 5; 165 }) 166 // After the userName property is modified in the parent component, clicking the name button synchronizes the parent's class property changes. 167 Button(`Child name: ${this.name}`) 168 .onClick(() => { 169 this.name = this.message.userName; 170 }) 171 // Clicking the button deletes the object with key Message from AppStorageV2. 172 Button('remove key: Message') 173 .onClick(() => { 174 AppStorageV2.remove<Message>(Message); 175 }) 176 // Clicking the button adds an object with key Message to AppStorageV2. 177 Button('connect key: Message') 178 .onClick(() => { 179 this.message = AppStorageV2.connect<Message>(Message, () => new Message(10, 'Lucy'))!; 180 }) 181 } 182 .width('100%') 183 .height('100%') 184 } 185} 186``` 187 188### Storing Data Between Two Pages 189 190Data page 191```ts 192// Data center. 193// Sample.ets 194@ObservedV2 195export class Sample { 196 @Trace p1: number = 0; 197 p2: number = 10; 198} 199``` 200 201Page 1 202```ts 203// Page1.ets 204import { AppStorageV2 } from '@kit.ArkUI'; 205import { Sample } from '../Sample'; 206 207@Entry 208@ComponentV2 209struct Page1 { 210 // Create a key-value pair with Sample as the key in AppStorageV2 (if the key exists, existing data in AppStorageV2 is returned) and associate it with prop. 211 @Local prop: Sample = AppStorageV2.connect(Sample, () => new Sample())!; 212 pageStack: NavPathStack = new NavPathStack(); 213 214 build() { 215 Navigation(this.pageStack) { 216 Column() { 217 Button('Go to page2') 218 .onClick(() => { 219 this.pageStack.pushPathByName('Page2', null); 220 }) 221 222 Button('Page1 connect the key Sample') 223 .onClick(() => { 224 // Create a key-value pair with Sample as the key in AppStorageV2 (if the key exists, existing data in AppStorageV2 is returned) and associate it with prop. 225 this.prop = AppStorageV2.connect(Sample, 'Sample', () => new Sample())!; 226 }) 227 228 Button('Page1 remove the key Sample') 229 .onClick(() => { 230 // After deletion from AppStorageV2, prop will no longer be associated with the value whose key is Sample. 231 AppStorageV2.remove(Sample); 232 }) 233 234 Text(`Page1 add 1 to prop.p1: ${this.prop.p1}`) 235 .fontSize(30) 236 .onClick(() => { 237 this.prop.p1++; 238 }) 239 240 Text(`Page1 add 1 to prop.p2: ${this.prop.p2}`) 241 .fontSize(30) 242 .onClick(() => { 243 // The page is not re-rendered, but the value of p2 is changed. 244 this.prop.p2++; 245 }) 246 247 // Obtain all current keys in AppStorageV2. 248 Text(`all keys in AppStorage: ${AppStorageV2.keys()}`) 249 .fontSize(30) 250 } 251 } 252 } 253} 254``` 255 256Page 2 257```ts 258// Page2.ets 259import { AppStorageV2 } from '@kit.ArkUI'; 260import { Sample } from '../Sample'; 261 262@Builder 263export function Page2Builder() { 264 Page2() 265} 266 267@ComponentV2 268struct Page2 { 269 // Create a key-value pair with Sample as the key in AppStorageV2 (if the key exists, existing data in AppStorageV2 is returned) and associate it with prop. 270 @Local prop: Sample = AppStorageV2.connect(Sample, () => new Sample())!; 271 pathStack: NavPathStack = new NavPathStack(); 272 273 build() { 274 NavDestination() { 275 Column() { 276 Button('Page2 connect the key Sample1') 277 .onClick(() => { 278 // Create a key-value pair with Sample1 as the key in AppStorageV2 (if the key exists, existing data in AppStorageV2 is returned) and associate it with prop. 279 this.prop = AppStorageV2.connect(Sample, 'Sample1', () => new Sample())!; 280 }) 281 282 Text(`Page2 add 1 to prop.p1: ${this.prop.p1}`) 283 .fontSize(30) 284 .onClick(() => { 285 this.prop.p1++; 286 }) 287 288 Text(`Page2 add 1 to prop.p2: ${this.prop.p2}`) 289 .fontSize(30) 290 .onClick(() => { 291 // The page is not re-rendered, but the value of p2 is changed, which is performed after re-initialization. 292 this.prop.p2++; 293 }) 294 295 // Obtain all current keys in AppStorageV2. 296 Text(`all keys in AppStorage: ${AppStorageV2.keys()}`) 297 .fontSize(30) 298 } 299 } 300 .onReady((context: NavDestinationContext) => { 301 this.pathStack = context.pathStack; 302 }) 303 } 304} 305``` 306When using **Navigation**, create a **route_map.json** file as shown below in the **src/main/resources/base/profile** directory, replacing the value of **pageSourceFile** with the actual path to **Page2**. Then, add **"routerMap": "$profile: route_map"** to the **module.json5** file. 307```json 308{ 309 "routerMap": [ 310 { 311 "name": "Page2", 312 "pageSourceFile": "src/main/ets/pages/Page2.ets", 313 "buildFunction": "Page2Builder", 314 "data": { 315 "description" : "AppStorageV2 example" 316 } 317 } 318 ] 319} 320``` 321