• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Mixing Use of State Management V1 and V2
2
3## Overview
4
5During the evolution of the state management framework, state managements V1 and V2 are launched based on API version 7 and API version 12, respectively. For applications that have used state management V1 and need to be migrated to state management V2, see [Migrating Applications from V1 to V2](./arkts-v1-v2-migration.md).
6
7For large-scale applications, V1 and V2 may be used together during the migration. In versions earlier than API version 18, strict verification is performed in the mixed use scenario, mainly in the transfer of complex objects. For details, see [Mixing Use of Custom Components](./arkts-custom-component-mixed-scenarios.md). To facilitate smooth migration to V2, API version 18 and later versions reduce the restrictions on the mixed use of V1 and V2. In addition, new methods [enableV2Compatibility](../../reference/apis-arkui/js-apis-StateManagement.md#enablev2compatibility18) and [makeV1Observed](../../reference/apis-arkui/js-apis-StateManagement.md#makev1observed18) are provided to help you solve related problems.
8
9> **NOTE**
10>
11> In this topic, the symbol "->" is used to indicate the transfer of variables. For example, "V1 -> V2" indicates that the state variable of V1 is transferred to the V2.
12
13
14## Verification Rules
15In versions earlier than API version 18, the mixed use rules of state managements V1 and V2 can be summarized as follows:
161. Decorators of V1 cannot be used together with @ObservedV2.
172. Decorators of V2 cannot be used together with @Observed.
183. Only simple types can be transferred from V1 to V2. Complex types (built-in types), including Array, Map, Set, and Date, are not allowed.
194. Simple types or a common class can be transferred from V2 to V1, but built-in types such as Array, Map, Set, or Date are not allowed.
20
21Since API version 18, only the first rule is still enabled, and the rest rules are open for verification. The following table lists the verification during compilation.
22
23| Scenario | Earlier than API Version 18| API Version 18 and Later |
24|------|----|------|
25| Decorators of V1 and \@ObservedV2 are used together.  | An error is reported.| An error is reported.|
26| Decorators of V2 and \@Observed are used together.| An error is reported.| No error is reported.|
27| A common class is transferred from V1 to V2. | An error is reported.| No error is reported.|
28| Built-in types such as Array, Map, Set, Date are transferred from V1 to V2. | An error is reported.| No error is reported.|
29| A \@Observed decorated class is transferred from V1 to V2. | An error is reported.| No error is reported.|
30| A \@ObservedV2 decorated class is transferred from V2 to V1. | An error is reported.| An error is reported.|
31| Built-in types such as Array, Map, Set, Date are transferred from V2 to V1. | An error is reported.| No error is reported.|
32| \@ObjectLink is initialized by a class that is not decorated by \@Observed. | An error is reported.| No error is reported.|
33
34@ObservedV2 or @Trace has its own independent observation capability, which can be used in both \@ComponentV2 and \@Component. However, the state management framework does not allow the mixed use of observation capability of V1 and V2. Therefore, the first rule is still enabled.
35
36## Available APIs
37### makeV1Observed
38
39static makeV1Observed\<T extends object\>(source: T): T
40
41The [makeV1Observed](../../reference/apis-arkui/js-apis-StateManagement.md#makev1observed18) API encapsulates an unobservable object into an observable object of state management V1. **makeV1Observed** has the same capability as that of @Observed and its return value can be used to initialize @ObjectLink.
42
43>**NOTE**
44>
45>This API is supported since API version 18.
46
47**Description**
48- **makeV1Observed** is used together with **enableV2Compatibility** for V2 -> V1 transfer.
49- **makeV1Observed** converts a common class, Array, Map, Set, or Date type to a state variable of V1. Its capability is equivalent to \@Observed. Therefore, the return value can be used to initialize \@ObjectLink.
50- If the data received by **makeV1Observed** is already the state variable of V1, **makeV1Observed** returns itself without any change.
51- **makeV1Observed** does not execute recursively. It only wraps the first layer into the state variable of V1.
52
53**Constraints**
54- The [collections](../../reference/apis-arkts/js-apis-arkts-collections.md) type and [\@Sendable](../../arkts-utils/arkts-sendable.md) decorated classes are not supported.
55- Non-object types are not supported.
56- **undefined** and **null** are not supported.
57- The return values of \@ObservedV2 and [makeObserved](../../reference/apis-arkui/js-apis-StateManagement.md#makeobserved), and variables of built-in types (such as Array, Map, Set, and Date) decorated by the decorators of V2 are not supported.
58
59
60### enableV2Compatibility
61
62static enableV2Compatibility\<T extends object\>(source: T): T
63
64[enableV2Compatibility](../../reference/apis-arkui/js-apis-StateManagement.md#enablev2compatibility18) enables the observation capability of V2 for the state variable of V1, that is, the state variable of V1 can be observed in \@ComponentV2.
65
66>**NOTE**
67>
68>This API is supported since API version 18.
69
70**Description**
71- This API is mainly used in the V1 -> V2 transfer. After the state variable of V1 calls this API and is transferred to \@ComponentV2, the change can be observed in V2 to implement associated data update.
72- **enableV2Compatibility** applies only to state variables of V1. The state variable of V1 is a variable decorated by the decorator of V1, such as \@Observed, \@State, \@Prop, \@Link, \@Provide, \@Consume, and \@ObjectLink (\@ObjectLink must be an instance decorated by \@Observed or the return value of **makeV1Observed**). Otherwise, the input parameter itself is returned.
73- **enableV2Compatibility** recursively traverses all properties of the class and all subitems of Array, Set, or Map until non-V1 state variable data is found.
74
75**Constraints**
76- Non-object types are not supported.
77- **undefined** and **null** are not supported.
78- Non-V1 state variable data is not supported.
79- The return values of \@ObservedV2 and [makeObserved](../../reference/apis-arkui/js-apis-StateManagement.md#makeobserved), and variables of built-in types (such as Array, Map, Set, and Date) decorated by the decorators of V2 are not supported.
80
81## Mixed Use Paradigm
82
83Based on the [enableV2Compatibility](../../reference/apis-arkui/js-apis-StateManagement.md#enablev2compatibility18) and [makeV1Observed](../../reference/apis-arkui/js-apis-StateManagement.md#makev1observed18) APIs, the mixed use paradigm of V1 and V2 is as follows:
84
85### V1->V2
86- The state variable of V1 is transferred to \@Param of V2. Call **UIUtils.enableV2Compatibility** to enable the state variable of V1 to be observed in \@ComponentV2. For details, see [Common Scenarios](#v1-v2-1).
87```ts
88import { UIUtils } from '@kit.ArkUI';
89
90@Observed
91class ObservedClass {
92}
93
94@Entry
95@Component
96struct CompV1 {
97  @State observedClass: ObservedClass = new ObservedClass();
98
99  build() {
100    Column() {
101      CompV2({ observedClass: UIUtils.enableV2Compatibility(this.observedClass) })
102    }
103  }
104}
105
106@ComponentV2
107struct CompV2 {
108  @Param observedClass: ObservedClass = new ObservedClass();
109
110  build() {
111  }
112}
113```
114- The state variables of V1 can be used to observe the first-layer properties. After **UIUtils.enableV2Compatibility** is called to transfer the variables to \@Param, \@Param is able to observe the changes of the first-layer properties.
115
116The following table lists the observation capability in specific scenarios after **enableV2Compatibility** is called.
117
118| \@Component (Parent) - > \@ComponentV2 (Child) | Observation Capability|
119|------|----|
120| Normal variable| No. **enableV2Compatibility** supports only state variables of V1.|
121| \@Observed decorated class  | The first-layer properties can be observed.|
122| Variable decorated by the decorator of V1, whose type is Array, Map, Set, or Date. | API calls can be observed.|
123| Variable decorated by the decorator of V1, whose type is class decorated by non-\@Observed. | The first-layer properties can be observed. Note that if the data source is \@ObjectLink, it must be the instance of the \@Observed decorated class or the return value of **makeV1Observed**.|
124| Normal array. The array item is the class decorated by \@Observed. | No. The outer array is a non-V1 state variable, so this API does not take effect and the data source itself is returned.|
125| Variable decorated by the decorator of V1, whose type is a normal array. The array item is the class decorated by \@Observed. | In \@Component, only the first layer can be observed. To observe the deeper layers, use \@ObjectLink together. You can observe the deeper layers in \@ComponentV2.|
126| \@ObservedV2 decorated class | Observable in V1 and V2. The observation capability is derived from the \@ObservedV2 and \@Trace capabilities. **enableV2Compatibility** does not take effect.|
127| Variable decorated by the decorator of V1, whose type is a normal array. The array item is the class decorated by \@ObservedV2.| Observable because **enableV2Compatibility** makes the outer array observable in V2. The property observation capability of the class decorated by \@ObservedV2 is derived from \@ObservedV2 and \@Trace, and is irrelevant to **enableV2Compatibility**.|
128
129### V2->V1
130
131For V2 -> V1 transfer, you are advised to use **UIUtils.enableV2Compatibility(UIUtils.makeV1Observed())**. If this object is observable in V1, only **UIUtils.enableV2Compatibility** needs to be called. For details, see [Common Scenarios](#v2-v1-1).
132
133```ts
134import { UIUtils } from '@kit.ArkUI';
135
136@Observed
137class ObservedClass {}
138
139@Entry
140@ComponentV2
141struct CompV2 {
142  @Local observedClass: ObservedClass = UIUtils.enableV2Compatibility(new ObservedClass());
143  build() {
144    Column() {
145      CompV1({ observedClass: this.observedClass })
146    }
147  }
148}
149
150@Component
151struct CompV1 {
152  @ObjectLink observedClass: ObservedClass;
153  build() {}
154}
155```
156
157The following table lists the specific scenarios.
158
159| \@ComponentV2 (Parent) -> \@Component (Child) | Observation Capability|
160|------|----|
161| \@Observed decorated nested class| In \@ComponentV2, you can observe the changes of nested properties.|
162| Normal class | Observable. Call **makeV1Observed** to execute **enableV2Compatibility** properly.|
163| Array\<number\> or other simple arrays | Observable. **makeV1Observed** needs to be called.<br>Example: **@Local local : Array\<number> = UIUtils.enableV2Compatibility(UIUtils.makeV1Observed([1, 2, 3]))**|
164| Array\<ObservedClass\> (The array item is a class decorated by \@Observed.) | Observable. **makeV1Observed** needs to be called.<br>Example: **@Local local : Array\<ObservedClass> = UIUtils.enableV2Compatibility(UIUtils.makeV1Observed([new ObservedClass()]))**|
165|  Array\<Array\<number\>\>: two-dimensional array, array item, or other simple types| Observable. **makeV1Observed** needs to be called.<br>Example: **@Local local : Array<Array\<number>>> = UIUtils.enableV2Compatibility(UIUtils.makeV1Observed([UIUtils.makeV1Observed([1, 2, 3])]))**|
166
167
168## Mixed Use Rules
169- When complex data is transferred from V1 to V2, call **enableV2Compatibility**; otherwise, data association between V1 and V2 cannot be implemented. It is recommended that **enableV2Compatibility** be called at the construction position of the V2 component. Otherwise, **enableV2Compatibility** needs to be manually called again when a value is assigned to the entire variable.
170
171```ts
172// Recommended. When this.state = new ObservedClass() is called, UIUtils.enableV2Compatibility is not required, simplifying the code.
173SubComponentV2({param: UIUtils.enableV2Compatibility(this.state)})
174
175// Not recommended. When a value is assigned to the state as a whole, UIUtils.enableV2Compatibility needs to be called again.
176// Otherwise, the variable of V1 transferred to SubComponentV2 cannot be observed in V2.
177// @State state: ObservedClass = UIUtils.enableV2Compatibility(new ObservedClass());
178// this.state = UIUtils.enableV2Compatibility(new ObservedClass())
179SubComponentV2({param: this.state})
180```
181
182- When complex data is transferred from V2 to V1, preferentially declare the state variable data of V1 in V2 and call **UIUtils.enableV2Compatibility**. This is because in state management V1, state variables have the capability of observing the first layer by default, while state management V2 has only the capability of observing itself. If you want to associate data between V1 and V2, you need to call **UIUtils.enableV2Compatibility(UIUtils.makeV1Observed())** to align the observation capabilities of both state managements.
183
184```ts
185// Recommended.
186@Local unObservedClass: UnObservedClass = UIUtils.enableV2Compatibility(UIUtils.makeV1Observed(new UnObservedClass()));
187
188// Recommended. ObservedClass is a class decorated by @Observed.
189@Local observedClass: ObservedClass = UIUtils.enableV2Compatibility(new ObservedClass());
190```
191- **UIUtils.enableV2Compatibility(UIUtils.makeV1Observed())** does not change the observation capabilities of V1 and V2.
192    - In V1, **UIUtils.enableV2Compatibility(UIUtils.makeV1Observed())** is equal to the observation capability of V1, which can observe the value assignment of the data and the first-layer property. If in-depth observation is required, \@ObjectLink should be used together.
193    - In V2, **UIUtils.enableV2Compatibility(UIUtils.makeV1Observed())** can be used to observe deeper layers, but each layer must be a class decorated by \@Observed or a return value of **makeV1Observed**.
194- After **UIUtils.enableV2Compatibility** is called, the observation capability of V2 is enabled for new data by default. However, you need to ensure that the new data is also the class decorated by \@Observed or the return value of **makeV1Observed**. For details about the complete example, see [Common Scenarios](#nested-type).
195```ts
196let arr: Array<ArrayItem> = UIUtils.enableV2Compatibility(UIUtils.makeV1Observed(new ArrayItem()));
197
198arr.push(new ArrayItem()); // The new data is not a state variable of V1. Therefore, the observation capability of V2 is unavailable.
199arr.push(UIUtils.makeV1Observed(new ArrayItem())); // The new data is the state variable of V1, which can be observed in V2 by default.
200```
201- For built-in types, such as Array, Map, Set, and Date, both V1 and V2 can observe the changes caused by their own value assignment and API calls. Although data refreshes can be implemented in some simple scenarios without calling **UIUtils.enableV2Compatibility**, dual proxies may cause poor performance. Therefore, **UIUtils.enableV2Compatibility(UIUtils.makeV1Observed())** is recommended. For details, see [Common Scenarios](#built-in-type).
202- For classes with \@Track decorated properties, the system does not crash when non-\@Track decorated properties are used in \@ComponentV2 but still crashes when they are used in \@Component. For details, see [Common Scenarios](#observed-decorated-class).
203
204When calling the two APIs to use V1 and V2 together, you can comply with the logic shown in the following figure.
205
206![mix-usage](./figures/V1V2_mix_usage.png)
207
208
209## Common Scenarios
210### Normal JS Object
211#### V1->V2
212**Recommended**
213
214```ts
215import { UIUtils } from '@kit.ArkUI';
216
217@Observed
218class ObservedClass {
219  name: string = 'Tom';
220}
221
222@Entry
223@Component
224struct CompV1 {
225  @State observedClass: ObservedClass = new ObservedClass();
226
227  build() {
228    Column() {
229      Text(`@State observedClass: ${this.observedClass.name}`)
230        .onClick(() => {
231          this.observedClass.name += '!'; // Refresh
232        })
233      // Call UIUtils.enableV2Compatibility to enable the state variable of V1 to be observed in @ComponentV2.
234      CompV2({ observedClass: UIUtils.enableV2Compatibility(this.observedClass) })
235    }
236  }
237}
238
239@ComponentV2
240struct CompV2 {
241  @Param observedClass: ObservedClass = new ObservedClass();
242
243  build() {
244    // After the observation capability of V2 is enabled for the state variable of V1, the first-layer changes can be observed in V2.
245    Text(`@Param observedClass: ${this.observedClass.name}`)
246      .onClick(() => {
247        this.observedClass.name += '!'; // Refresh
248      })
249  }
250}
251```
252**Not recommended**
253
254In the following example, when the state variable of V1 is transferred to V2, the **enableV2Compatibility** API is not called, and the observation capability of V2 is not enabled. Therefore, **observedClass** cannot observe the change of the **name** property in **CompV2**. The observation capabilities of the same state variable in **CompV1** and **CompV2** are different.
255
256```ts
257@Observed
258class ObservedClass {
259  name: string = 'Tom';
260}
261
262@Entry
263@Component
264struct CompV1 {
265  @State observedClass: ObservedClass = new ObservedClass();
266
267  build() {
268    Column() {
269      Text(`@State observedClass: ${this.observedClass.name}`)
270        .onClick(() => {
271          this.observedClass.name += '!'; // Refresh
272        })
273      // The enableV2Compatibility API is not called. The state variable of V1 cannot be observed in CompV2.
274      // The change of name cannot be observed in CompV2.
275      CompV2({ observedClass: this.observedClass })
276    }
277  }
278}
279
280@ComponentV2
281struct CompV2 {
282  @Param observedClass: ObservedClass = new ObservedClass();
283
284  build() {
285    Text(`@Param observedClass: ${this.observedClass.name}`)
286      .onClick(() => {
287        this.observedClass.name += '!'; // Do not refresh.
288      })
289  }
290}
291```
292#### V2->V1
293
294**Recommended**
295
296During V2->V1 transfer, to align the observation capabilities of V2 and V1, the **makeV1Observed** API needs to be called in V2. In addition, the observation capability of V2 needs to be enabled by calling the **enableV2Compatibility** API. Therefore, the recommended sample code is as follows:
297
298```ts
299import { UIUtils } from '@kit.ArkUI';
300
301class ObservedClass {
302  name: string = 'Tom';
303}
304
305@Entry
306@ComponentV2
307struct CompV2 {
308  @Local observedClass: ObservedClass = UIUtils.enableV2Compatibility(UIUtils.makeV1Observed(new ObservedClass()));
309
310  build() {
311    Column() {
312      // @Local can only observe itself.
313      // However, UIUtils.makeV1Observed is called to change @Local to the state variable of V1, whose first-layer changes are observable.
314      // Call UIUtils.enableV2Compatibility to make it observable in V2.
315      // Currently, you can observe the changes of the first-layer properties.
316      Text(`@Local observedClass: ${this.observedClass.name}`)
317        .onClick(() => {
318          this.observedClass.name += '!'; // Refresh
319        })
320      // @ObjectLink can receive the instance of the @Observed decorated class or the return value of makeV1Observed.
321      CompV1({ observedClass: this.observedClass })
322    }
323  }
324}
325
326@Component
327struct CompV1 {
328  @ObjectLink observedClass: ObservedClass;
329
330  build() {
331    // The change of the first layer can be observed in CompV1.
332    Text(`@ObjectLink observedClass: ${this.observedClass.name}`)
333      .onClick(() => {
334        this.observedClass.name += '!'; // Refresh
335      })
336  }
337}
338```
339**Not recommended**
340
341The observation capabilities of V1 and V2 are different. If **UIUtils.enableV2Compatibility(UIUtils.makeV1Observed())** is not called to directly transfer data, the data is not updated or the update behavior is inconsistent.
342
343```ts
344class ObservedClass {
345  name: string = 'Tom';
346}
347
348@Entry
349@ComponentV2
350struct CompV2 {
351  @Local observedClass: ObservedClass = new ObservedClass();
352
353  build() {
354    Column() {
355      // @Local can only observe itself. Property changes cannot be observed here.
356      Text(`@Local observedClass: ${this.observedClass.name}`)
357        .onClick(() => {
358          this.observedClass.name += '!'; // Do not refresh.
359        })
360      // @ObjectLink cannot receive instances of non-@Observed decorated classes or return values of makeV1Observed.
361      // The log indicates that the current ObjectLink is assigned an invalid value.
362      CompV1({ observedClass1: this.observedClass, observedClass2: this.observedClass })
363    }
364  }
365}
366
367@Component
368struct CompV1 {
369  @ObjectLink observedClass1: ObservedClass;
370  @State observedClass2: ObservedClass = new ObservedClass();
371
372  build() {
373    Column() {
374      // The value of @ObjectLink is invalid and does not respond to the UI re-render.
375      Text(`@ObjectLink observedClass: ${this.observedClass1.name}`)
376        .onClick(() => {
377          this.observedClass1.name += '!'; // Do not refresh.
378        })
379
380      // Different from @ObjectLink, @State wraps unobservable objects into observable objects of V1 by default. The changes of itself and attributes can be observed.
381      Text(`@State observedClass: ${this.observedClass2.name}`)
382        .onClick(() => {
383          this.observedClass2.name += '!'; // Refresh
384        })
385    }
386  }
387}
388```
389### \@Observed Decorated Class
390#### V1->V2
391In the following example:
392- **ObservedClass** is the class decorated by \@Observed and enables the observation capability in V2.
393- **name** is a @Track decorated property and is observable in both V1 and V2.
394- **count** is a non-@Track decorated property and is invalid in both V1 and V2.
395    - In V1, if a non-@Track decorated property is used on the UI, an error is reported during runtime.
396    - In V2, no error is reported during runtime when non-@Track decorated properties are used on the UI, but the refresh is not responded.
397
398```ts
399import { UIUtils } from '@kit.ArkUI';
400
401@Observed
402class ObservedClass {
403  @Track name: string = 'a';
404  count: number = 0;
405}
406
407@Entry
408@Component
409struct CompV1 {
410  @State observedClass: ObservedClass = new ObservedClass();
411  build() {
412    Column() {
413      Text(`name: ${this.observedClass.name}`).onClick(() => {
414        // Trigger the refresh.
415        this.observedClass.name += 'a';
416      })
417      // If a non-@Track decorated variable is used in V1, the system crashes.
418      // Text(`count: ${this.observedClass.count}`)
419
420      CompV2({ observedClass: UIUtils.enableV2Compatibility(this.observedClass) })
421    }
422  }
423}
424
425@ComponentV2
426struct CompV2 {
427  @Param observedClass: ObservedClass = new ObservedClass();
428  build() {
429    // The system does not crash when a non-@Track variable is used in V2, but the refresh is not responded as well.
430    Text(`count: ${this.observedClass.count}`).onClick(() => {
431      // No refresh is triggered.
432      this.observedClass.count++;
433    })
434  }
435}
436```
437#### V2->V1
438- **ObservedClass** is a class decorated by \@Observed. Therefore, when **UIUtils.enableV2Compatibility** is transferred to V1, **UIUtils.makeV1Observed** does not need to be called.
439- Only the \@Track decorated variables can be observed in V1 and V2. If a non-\@Track variable is used in V1, an error is reported on the UI. In V2, no error is reported but the variable does not respond to the refresh.
440```ts
441import { UIUtils } from '@kit.ArkUI';
442
443@Observed
444class ObservedClass {
445  @Track name: string = 'a';
446  count: number = 0;
447}
448
449@Entry
450@ComponentV2
451struct CompV1 {
452  @Local observedClass: ObservedClass = UIUtils.enableV2Compatibility(new ObservedClass());
453
454  build() {
455    Column() {
456      Text(`name: ${this.observedClass.name}`).onClick(() => {
457        // Trigger the refresh.
458        this.observedClass.name += 'a';
459      })
460      // The system does not crash when a non-@Track variable is used in V2, but the refresh is not triggered as well.
461      Text(`count: ${this.observedClass.count}`).onClick(() => {
462        this.observedClass.count++;
463      })
464
465      CompV2({ observedClass: this.observedClass })
466    }
467  }
468}
469
470@Component
471struct CompV2 {
472  @ObjectLink observedClass: ObservedClass;
473
474  build() {
475    Column() {
476      Text(`count: ${this.observedClass.name}`).onClick(() => {
477        // Trigger the refresh.
478        this.observedClass.name += 'a';
479      })
480      // If a non-@Track decorated variable is used in V1, the system crashes.
481      // Text(`count: ${this.observedClass.count}`)
482    }
483  }
484}
485```
486
487### Built-in Type
488The following uses Array as an example.
489#### V1->V2
490**Recommended**
491
492```ts
493import { UIUtils } from '@kit.ArkUI';
494
495@Entry
496@Component
497struct ArrayCompV1 {
498  @State arr: Array<number> = UIUtils.makeV1Observed([1, 2, 3]);
499
500  build() {
501    Column() {
502      Text(`V1 ${this.arr[0]}`).onClick(() => {
503        // Click to trigger the changes of ArrayCompV1 and ArrayCompV2.
504        this.arr[0]++;
505      })
506      // When the variable is transferred to V2, it is found that the current proxy is wrapped by makeV1Observed and the observation capability of V2 is enabled.
507      // In ArrayCompV2, Param does not wrap the proxy again to avoid the dual proxy problem.
508      ArrayCompV2({ arr: UIUtils.enableV2Compatibility(this.arr) })
509    }
510    .height('100%')
511    .width('100%')
512  }
513}
514
515@ComponentV2
516struct ArrayCompV2 {
517  @Param arr: Array<number> = [1, 2, 3];
518
519  build() {
520    Column() {
521      Text(`V2 ${this.arr[0]}`).onClick(() => {
522        // Click to trigger the changes of ArrayCompV1 and ArrayCompV2.
523        this.arr[0]++;
524      })
525    }
526  }
527}
528```
529**Not recommended**
530
531In the following example, enableV2Compatibility and makeV1Observed are not called. Therefore, the proxy and refresh in V1 and V2 are inconsistent.
532```ts
533@Entry
534@Component
535struct ArrayCompV1 {
536  @State arr: Array<number> = [1, 2, 3];
537
538  build() {
539    Column() {
540      Text(`V1 ${this.arr[0]}`).onClick(() => {
541        // V1 proxy, which can trigger the refresh of ArrayCompV1 but cannot trigger the refresh of ArrayCompV2.
542        this.arr[0]++;
543      })
544      // After being transferred to ArrayCompV2, the variable will be wrapped with a proxy of V2.
545      ArrayCompV2({ arr: this.arr })
546    }
547    .height('100%')
548    .width('100%')
549  }
550}
551
552@ComponentV2
553struct ArrayCompV2 {
554  @Param arr: Array<number> = [1, 2, 3];
555
556  build() {
557    Column() {
558      Text(`V2 ${this.arr[0]}`).onClick(() => {
559        // V1 and V2 proxy, which can trigger refresh of ArrayCompV1 or ArrayCompV2.
560        this.arr[0]++;
561      })
562    }
563  }
564}
565```
566#### V2->V1
567**Recommended**
568
569```ts
570import { UIUtils } from '@kit.ArkUI';
571
572@Entry
573@ComponentV2
574struct ArrayCompV2 {
575  @Local arr: Array<number> = UIUtils.enableV2Compatibility(UIUtils.makeV1Observed([1, 2, 3]));
576
577  build() {
578    Column() {
579      Text(`V2 ${this.arr[0]}`).fontSize(20).onClick(() => {
580        // Click to trigger changes in V2 and synchronize the change to the @ObjectLink of V1.
581        this.arr[0]++;
582      })
583      ArrayCompV1({ arr: this.arr })
584    }
585    .height('100%')
586    .width('100%')
587  }
588}
589
590@Component
591struct ArrayCompV1 {
592  @ObjectLink arr: Array<number>;
593
594  build() {
595    Column() {
596      Text(`V1 ${this.arr[0]}`).fontSize(20).onClick(() => {
597        // Click to trigger the change of V1 and synchronize the change to V2 in a two-way manner.
598        this.arr[0]++;
599      })
600    }
601  }
602}
603
604```
605**Not recommended**
606
607In the following example, **enableV2Compatibility** and **makeV1Observed** are not called, and \@ObjectLink is initialized illegally so that it cannot observe the property changes.
608However, because the state variables of V2 are transferred to \@ObjectLink, the refresh in V2 can be triggered.
609```ts
610@Entry
611@ComponentV2
612struct ArrayCompV2 {
613  @Local arr: Array<number> = [1, 2, 3];
614
615  build() {
616    Column() {
617      Text(`V2 ${this.arr[0]}`).fontSize(20).onClick(() => {
618        // Click to trigger changes in V2.
619        this.arr[0]++;
620      })
621      // The data passed to @ObjectLink is not @Observed or makeV1Observed data
622      // Invalid operation. @ObjectLink cannot observe property changes.
623      ArrayCompV1({ arr: this.arr })
624    }
625    .height('100%')
626    .width('100%')
627  }
628}
629
630@Component
631struct ArrayCompV1 {
632  @ObjectLink arr: Array<number>;
633
634  build() {
635    Column() {
636      Text(`V1 ${this.arr[0]}`).fontSize(20).onClick(() => {
637        // V2 is refreshed while V1 is not.
638        this.arr[0]++;
639      })
640    }
641  }
642}
643```
644### Two-Dimensional Array
645#### V1->V2
646
647In the following example:
648- **makeV1Observed** is used to change the inner array of the two-dimensional array to the state variables of V1.
649- When the state variables of V1 are passed to the child component of V2, **enableV2Compatibility** is called to make them observable in V2 and avoid the dual proxy of V1 and V2.
650
651```ts
652import { UIUtils } from '@kit.ArkUI';
653
654@ComponentV2
655struct Item {
656  @Require @Param itemArr: Array<string>;
657
658  build() {
659    Row() {
660      ForEach(this.itemArr, (item: string, index: number) => {
661        Text(`${index}: ${item}`)
662      }, (item: string) => item + Math.random())
663
664      Button('@Param push')
665        .onClick(() => {
666          this.itemArr.push('Param');
667        })
668    }
669  }
670}
671
672@Entry
673@Component
674struct IndexPage {
675  @State arr: Array<Array<string>> =
676    [UIUtils.makeV1Observed(['apple']), UIUtils.makeV1Observed(['banana']), UIUtils.makeV1Observed(['orange'])];
677
678  build() {
679    Column() {
680      ForEach(this.arr, (itemArr: Array<string>) => {
681        Item({ itemArr: UIUtils.enableV2Compatibility(itemArr) })
682      }, (itemArr: Array<string>) => JSON.stringify(itemArr) + Math.random())
683      Divider()
684      Button('@State push two-dimensional array item')
685        .onClick(() => {
686          this.arr[0].push('strawberry');
687        })
688
689      Button('@State push array item')
690        .onClick(() => {
691          this.arr.push(UIUtils.makeV1Observed(['pear']));
692        })
693
694      Button('@State change two-dimensional array first item')
695        .onClick(() => {
696          this.arr[0][0] = 'APPLE';
697        })
698
699      Button('@State change array first item')
700        .onClick(() => {
701          this.arr[0] = UIUtils.makeV1Observed(['watermelon']);
702        })
703    }
704  }
705}
706```
707
708#### V2->V1
709
710In the following example:
711- **makeV1Observed** is used to change the inner array of the two-dimensional array to the state variables of V1. **enableV2Compatibility** is called to make them observable in V2 and avoid the dual proxy of V1 and V2.
712- In V1, \@ObjectLink is used to receive the inner array of the two-dimensional array. Because the inner array is the return value of **makeV1Observed**, the refresh response is normal when **Button('@ObjectLink push')** is clicked.
713
714```ts
715import { UIUtils } from '@kit.ArkUI';
716
717@Component
718struct Item {
719  @ObjectLink itemArr: Array<string>;
720
721  build() {
722    Row() {
723      ForEach(this.itemArr, (item: string, index: number) => {
724        Text(`${index}: ${item}`)
725      }, (item: string) => item + Math.random())
726
727      Button('@ObjectLink push')
728        .onClick(() => {
729          this.itemArr.push('ObjectLink');
730        })
731    }
732  }
733}
734
735@Entry
736@ComponentV2
737struct IndexPage {
738  @Local arr: Array<Array<string>> =
739    UIUtils.enableV2Compatibility(UIUtils.makeV1Observed([UIUtils.makeV1Observed(['apple']),
740      UIUtils.makeV1Observed(['banana']), UIUtils.makeV1Observed(['orange'])]));
741
742  build() {
743    Column() {
744      ForEach(this.arr, (itemArr: Array<string>) => {
745        Item({ itemArr: itemArr })
746      }, (itemArr: Array<string>) => JSON.stringify(itemArr) + Math.random())
747      Divider()
748      Button('@Local push two-dimensional array item')
749        .onClick(() => {
750          this.arr[0].push('strawberry');
751        })
752
753      Button('@Local push array item')
754        .onClick(() => {
755          this.arr.push(UIUtils.makeV1Observed(['pear']));
756        })
757
758      Button('@Local change two-dimensional array first item')
759        .onClick(() => {
760          this.arr[0][0] = 'APPLE';
761        })
762
763      Button('@Local change array first item')
764        .onClick(() => {
765          this.arr[0] = UIUtils.makeV1Observed(['watermelon']);
766        })
767    }
768  }
769}
770```
771
772### Nested Type
773#### V1->V2
774The following shows an example of the nested scenario,
775which can be summarized as follows:
776- \@State can observe only the first-layer changes. To perform in-depth observation, variables should be transferred to \@ObjectLink.
777- The change of the second layer of \@State cannot cause the refresh of the current layer. However, the change can be observed by \@ObjectLink and \@Param and triggers the re-render of the associated components.
778- \@ObjectLink and \@Param are referenced by the same object. If their properties are changed, other references are refreshed.
779- After **enableV2Compatibility** is enabled, V2 has the in-depth observation capability.
780- If **enableV2Compatibility** is not called when transferring the object to V2, **Param** cannot observe the object properties.
781```ts
782// Not recommended.
783NestedClassV2({ outer: this.outer })
784```
785A complete example is as follows:
786```ts
787import { UIUtils } from '@kit.ArkUI';
788
789class ArrayItem {
790  value: number = 0;
791
792  constructor(value: number) {
793    this.value = value;
794  }
795}
796
797class Inner {
798  innerValue: string = 'inner';
799  arr: Array<ArrayItem>;
800
801  constructor(arr: Array<ArrayItem>) {
802    this.arr = arr;
803  }
804}
805
806class Outer {
807  @Track outerValue: string = 'out';
808  @Track inner: Inner;
809
810  constructor(inner: Inner) {
811    this.inner = inner;
812  }
813}
814
815@Entry
816@Component
817struct NestedClassV1 {
818  // Ensure that each layer uses the state variable of V1.
819  @State outer: Outer =
820    UIUtils.makeV1Observed(new Outer(
821      UIUtils.makeV1Observed(new Inner(UIUtils.makeV1Observed([
822        UIUtils.makeV1Observed(new ArrayItem(1)),
823        UIUtils.makeV1Observed(new ArrayItem(2))
824      ])))
825    ));
826
827  build() {
828    Column() {
829      Text(`@State outer.outerValue can update ${this.outer.outerValue}`)
830        .fontSize(20)
831        .onClick(() => {
832          // @State can observe the first-layer changes.
833          // Notify @ObjectLink and @Param of the change.
834          this.outer.outerValue += '!';
835        })
836
837      Text(`@State outer.inner.innerValue cannot update ${this.outer.inner.innerValue}`)
838        .fontSize(20)
839        .onClick(() => {
840          // @State cannot observe the changes at the second layer.
841          // However, the change will be observed by @ObjectLink and @Param.
842          this.outer.inner.innerValue += '!';
843        })
844      // Transfer inner to @ObjectLink to observe the property change of inner.
845      NestedClassV1ObjectLink({ inner: this.outer.inner })
846      // Pass the data for enabling enableV2Compatibility to V2.
847      NestedClassV2({ outer: UIUtils.enableV2Compatibility(this.outer) })
848    }
849    .height('100%')
850    .width('100%')
851  }
852}
853
854@Component
855struct NestedClassV1ObjectLink {
856  @ObjectLink inner: Inner;
857
858  build() {
859    Text(`@ObjectLink inner.innerValue can update ${this.inner.innerValue}`)
860      .fontSize(20)
861      .onClick(() => {
862        // Trigger refresh. @ObjectLink and @Param are referenced by the same object and @Param is also refreshed.
863        this.inner.innerValue += '!';
864      })
865  }
866}
867
868@ComponentV2
869struct NestedClassV2 {
870  @Require @Param outer: Outer;
871
872  build() {
873    Column() {
874      Text(`@Param outer.outerValue can update ${this.outer.outerValue}`)
875        .fontSize(20)
876        .onClick(() => {
877          // The value changes at the first layer can be observed.
878          this.outer.outerValue += '!';
879        })
880      Text(`@Param outer.inner.innerValue can update ${this.outer.inner.innerValue}`)
881        .fontSize(20)
882        .onClick(() => {
883          // Changes on the second layer can be obtained. @Param and @ObjectLink are referenced by the same object, which also triggers a refresh.
884          this.outer.inner.innerValue += '!';
885        })
886
887      Repeat(this.outer.inner.arr)
888        .each((item: RepeatItem<ArrayItem>) => {
889          Text(`@Param outer.inner.arr index: ${item.index} item: ${item.item.value}`)
890        })
891
892      Button('@Param push').onClick(() => {
893        // The observation capability of V2 has been enabled for outer. For new data, the observation capability of V2 is enabled by default.
894        this.outer.inner.arr.push(UIUtils.makeV1Observed(new ArrayItem(20)));
895      })
896
897      Button('@Param change the last Item').onClick(() => {
898        // The property change of the last array item can be observed.
899        this.outer.inner.arr[this.outer.inner.arr.length - 1].value++;
900      })
901    }
902  }
903}
904```
905
906#### V2->V1
907- In the following example, **outer** in **NestedClassV2** calls **UIUtils.enableV2Compatibility**, and **UIUtils.makeV1Observed** is used in each layer. Therefore, **outer** has the in-depth observation capability in V2.
908- In V1, only the changes at the first layer can be observed. Therefore, multiple layers of custom components are required, and each layer uses \@ObjectLink to receive data to implement in-depth observation.
909
910```ts
911import { UIUtils } from '@kit.ArkUI';
912
913class ArrayItem {
914  value: number = 0;
915
916  constructor(value: number) {
917    this.value = value;
918  }
919}
920
921class Inner {
922  innerValue: string = 'inner';
923  arr: Array<ArrayItem>;
924
925  constructor(arr: Array<ArrayItem>) {
926    this.arr = arr;
927  }
928}
929
930class Outer {
931  @Track outerValue: string = 'out';
932  @Track inner: Inner;
933
934  constructor(inner: Inner) {
935    this.inner = inner;
936  }
937}
938
939@Entry
940@ComponentV2
941struct NestedClassV2 {
942  // Ensure that each layer uses the state variable of V1.
943  @Local outer: Outer = UIUtils.enableV2Compatibility(
944    UIUtils.makeV1Observed(new Outer(
945      UIUtils.makeV1Observed(new Inner(UIUtils.makeV1Observed([
946        UIUtils.makeV1Observed(new ArrayItem(1)),
947        UIUtils.makeV1Observed(new ArrayItem(2))
948      ])))
949    )));
950
951  build() {
952    Column() {
953      Text(`@Local outer.outerValue can update ${this.outer.outerValue}`)
954        .fontSize(20)
955        .onClick(() => {
956          // The changes at the first layer can be observed.
957          this.outer.outerValue += '!';
958        })
959
960      Text(`@Local outer.inner.innerValue can update ${this.outer.inner.innerValue}`)
961        .fontSize(20)
962        .onClick(() => {
963          // The changes at the second layer can be observed.
964          this.outer.inner.innerValue += '!';
965        })
966      // Transfer inner to @ObjectLink to observe the property change of inner.
967      NestedClassV1ObjectLink({ inner: this.outer.inner })
968    }
969    .height('100%')
970    .width('100%')
971  }
972}
973
974@Component
975struct NestedClassV1ObjectLink {
976  @ObjectLink inner: Inner;
977
978  build() {
979    Column() {
980      Text(`@ObjectLink inner.innerValue can update ${this.inner.innerValue}`)
981        .fontSize(20)
982        .onClick(() => {
983          // Trigger the refresh.
984          this.inner.innerValue += '!';
985        })
986      NestedClassV1ObjectLinkArray({ arr: this.inner.arr })
987    }
988  }
989}
990
991@Component
992struct NestedClassV1ObjectLinkArray {
993  @ObjectLink arr: Array<ArrayItem>;
994
995  build() {
996    Column() {
997      ForEach(this.arr, (item: ArrayItem) => {
998        NestedClassV1ObjectLinkArrayItem({ item: item })
999      }, (item: ArrayItem, index: number) => {
1000        return item.value.toString() + index.toString();
1001      })
1002
1003      Button('@ObjectLink push').onClick(() => {
1004        this.arr.push(UIUtils.makeV1Observed(new ArrayItem(20)));
1005      })
1006
1007      Button('@ObjectLink change the last Item').onClick(() => {
1008        // Observe the property change of the last array item in NestedClassV1ObjectLinkArrayItem.
1009        this.arr[this.arr.length - 1].value++;
1010      })
1011    }
1012  }
1013}
1014
1015@Component
1016struct NestedClassV1ObjectLinkArrayItem {
1017  @ObjectLink item: ArrayItem;
1018
1019  build() {
1020    Text(`@ObjectLink outer.inner.arr item: ${this.item.value}`)
1021  }
1022}
1023
1024```
1025
1026