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 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