• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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