1# \@Prop Decorator: One-Way Synchronization from the Parent Component to Child Components 2 3 4One-way synchronization is supported between an \@Prop decorated variable a variable of its parent component. This means that, an \@Prop decorated variable is mutable, and its changes will not be synchronized to the parent component. 5 6 7> **NOTE** 8> 9> This decorator can be used in ArkTS widgets since API version 9. 10> 11> This decorator can be used in atomic services since API version 11. 12 13## Overview 14 15For the \@Prop decorated variable of a child component, the change synchronization to the parent component is uni-directional. 16 17- An \@Prop variable is allowed to be modified locally, but the change does not propagate back to its parent component. 18 19- Whenever the data source changes, the \@Prop decorated variable gets updated, and any locally made changes are overwritten. In other words, the change is synchronized from the parent component to the (owning) child component, but not the other way around. 20 21## Restrictions 22 23- When decorating variables, \@Prop makes a deep copy, during which all types, except primitive types, Map, Set, Date, and Array, will be lost. For example, for complex types provided by N-API, such as [PixelMap](../reference/apis-image-kit/js-apis-image.md#pixelmap7), because they are partially implemented in the native code, complete data cannot be obtained through a deep copy in ArkTS. 24 25- The \@Prop decorator cannot be used in custom components decorated by \@Entry. 26 27 28## Rules of Use 29 30| \@Prop Decorator| Description | 31| ----------- | ---------------------------------------- | 32| Decorator parameters | None. | 33| Synchronization type | One-way: from the data source provided by the parent component to the \@Prop decorated variable. For details about the scenarios of nested types, see [Observed Changes](#observed-changes).| 34| Allowed variable types | Object, class, string, number, Boolean, enum, and array of these types.<br>**undefined** or **null** (**any** is not supported).<br>Date type.<br>(Applicable to API version 11 or later) Map and Set types.<br>The union types defined by the ArkUI framework, including Length, ResourceStr, and ResourceColor, are supported.<br>The type must be specified.<br>The type must be the same as that of the [data source](arkts-state-management-overview.md#basic-concepts). There are three cases:<br>- Synchronizing the \@Prop decorated variable from a variable decorated by \@State or other decorators. Example: [Simple Type @Prop Synced from @State in Parent Component](#simple-type-prop-synced-from-state-in-parent-component).<br>- Synchronizing the \@Prop decorated variable from the item of an array decorated by an \@State or other decorators. Example: [Simple Type @Prop Synced from @State Array Item in Parent Component](#simple-type-prop-synced-from-state-array-item-in-parent-component).<br>- Synchronizing the \@Prop decorated variable from a state property of the Object or class type in the parent component. Example: [Class Object Type @Prop Synced from @State Class Object Property in Parent Component](#class-object-type-prop-synced-from-state-class-object-property-in-parent-component).<br>For details about the scenarios of supported types, see [Observed Changes](#observed-changes).<br>(Applicable to API version 11 or later) Union type of the preceding types, for example, **string \| number**, **string \| undefined** or **ClassA \| null**. For details, see [Union Type @Prop](#union-type-prop).<br>**NOTE**<br>When **undefined** or **null** is used, you are advised to explicitly specify the type to pass the TypeScript type check. For example, **@Prop a: string \| undefined = undefined** is recommended; **@Prop a: string = undefined** is not recommended.| 35| Number of nested layers | In component reuse scenarios, it is recommended that @Prop be nested with no more than five layers of data. If @Prop is nested with too many layers of data, garbage collection and increased memory usage caused by deep copy will arise, resulting in performance issues. To avoid such issues, use [\@ObjectLink](arkts-observed-and-objectlink.md) instead.| 36| Initial value for the decorated variable | Local initialization is allowed. If this decorator is used together with [\@Require](arkts-require.md) in API version 11, the parent component must construct input parameters.| 37 38 39## Variable Transfer/Access Rules 40 41| Transfer/Access | Description | 42| --------- | ---------------------------------------- | 43| Initialization from the parent component | Optional if local initialization is used and mandatory otherwise. An @Prop decorated variable can be initialized from a regular variable (whose change does not trigger UI refresh) or an [\@State](arkts-state.md), [\@Link](arkts-link.md), @Prop, [\@Provide](arkts-provide-and-consume.md), [\@Consume](arkts-provide-and-consume.md), [\@ObjectLink](arkts-observed-and-objectlink.md), [\@StorageLink](arkts-appstorage.md#storagelink), [\@StorageProp](arkts-appstorage.md#storageprop), [\@LocalStorageLink](arkts-localstorage.md#localstoragelink), or [\@LocalStorageProp](arkts-localstorage.md#localstorageprop) decorated variable in its parent component.| 44| Child component initialization | \@Prop can be used for initialization of a regular variable or \@State, \@Link, \@Prop, or \@Provide decorated variable in the child component.| 45| Access| Private, accessible only within the component. | 46 47 48 **Figure 1** Initialization rule 49 50 51 52 53 54## Observed Changes and Behavior 55 56 57### Observed Changes 58 59\@Prop decorated variables can observe the following changes: 60 61- When the decorated variable is of the Object, class, string, number, Boolean, or enum type, its value change can be observed. 62 63 ```ts 64 // Simple type 65 @Prop count: number; 66 // The value change can be observed. 67 this.count = 1; 68 // Complex type 69 @Prop title: Model; 70 // The value change can be observed. 71 this.title = new Model('Hi'); 72 ``` 73 74- When the decorated variable is of the Object or class type, the value changes of properties at the first layer, that is, the properties that **Object.keys(observedObject)** returns, can be observed. 75 76``` 77class ClassA { 78 public value: string; 79 constructor(value: string) { 80 this.value = value; 81 } 82} 83class Model { 84 public value: string; 85 public a: ClassA; 86 constructor(value: string, a: ClassA) { 87 this.value = value; 88 this.a = a; 89 } 90} 91 92@Prop title: Model; 93// The value changes at the first layer can be observed. 94this.title.value = 'Hi' 95// The value changes at the second layer cannot be observed. 96this.title.a.value = 'ArkUi' 97``` 98 99In the scenarios of nested objects, if a class is decorated by \@Observed, the value changes of the class property can be observed. For details, see [@Prop Nesting Scenario](#prop-nesting-scenario). 100 101- When the decorated variable is of the array type, the value change of the array as well as the addition, deletion, and update of array items can be observed. 102 103``` 104// Assume that the object decorated by @State is an array. 105@Prop title: string[] 106// The value change of the array itself can be observed. 107this.title = ['1'] 108// The value change of array items can be observed. 109this.title[0] = '2' 110// The deletion of array items can be observed. 111this.title.pop() 112// The addition of array items can be observed. 113this.title.push('3') 114``` 115 116For synchronization between \@State and \@Prop decorated variables: 117 118- The value of an \@State decorated variable in the parent component is used to initialize an \@Prop decorated variable in the child component. Any change to an \@State decorated variable is updated to the @Prop decorated variable. 119- However, any change to the @Prop decorated variable does not affect the value of its source @State decorated variable. 120- In addition to \@State, the source can also be decorated with \@Link or \@Prop, where the mechanisms for syncing the \@Prop decorated variable is the same. 121- The source and \@Prop decorated variable must be of the same type. The \@Prop decorated variable can be of simple and class types. 122 123- When the decorated variable is of the Date type, the value change of the **Date** object can be observed, and the following APIs can be called to update **Date** properties: **setFullYear**, **setMonth**, **setDate**, **setHours**, **setMinutes**, **setSeconds**, **setMilliseconds**, **setTime**, **setUTCFullYear**, **setUTCMonth**, **setUTCDate**, **setUTCHours**, **setUTCMinutes**, **setUTCSeconds**, and **setUTCMilliseconds**. 124 125```ts 126@Component 127struct DateComponent { 128 @Prop selectedDate: Date = new Date(''); 129 130 build() { 131 Column() { 132 Button('child update the new date') 133 .margin(10) 134 .onClick(() => { 135 this.selectedDate = new Date('2023-09-09') 136 }) 137 Button(`child increase the year by 1`).onClick(() => { 138 this.selectedDate.setFullYear(this.selectedDate.getFullYear() + 1) 139 }) 140 DatePicker({ 141 start: new Date('1970-1-1'), 142 end: new Date('2100-1-1'), 143 selected: this.selectedDate 144 }) 145 } 146 } 147} 148 149@Entry 150@Component 151struct ParentComponent { 152 @State parentSelectedDate: Date = new Date('2021-08-08'); 153 154 build() { 155 Column() { 156 Button('parent update the new date') 157 .margin(10) 158 .onClick(() => { 159 this.parentSelectedDate = new Date('2023-07-07') 160 }) 161 Button('parent increase the day by 1') 162 .margin(10) 163 .onClick(() => { 164 this.parentSelectedDate.setDate(this.parentSelectedDate.getDate() + 1) 165 }) 166 DatePicker({ 167 start: new Date('1970-1-1'), 168 end: new Date('2100-1-1'), 169 selected: this.parentSelectedDate 170 }) 171 172 DateComponent({ selectedDate: this.parentSelectedDate }) 173 } 174 175 } 176} 177``` 178 179- When the decorated variable is **Map**, value changes of **Map** can be observed. In addition, you can call the **set**, **clear**, and **delete** APIs of **Map** to update its value. For details, see [Decorating Variables of the Map Type](#decorating-variables-of-the-map-type). 180 181- When the decorated variable is **Set**, value changes of **Set** can be observed. In addition, you can call the **add**, **clear**, and **delete** APIs of **Set** to update its value. For details, see [Decorating Variables of the Set Type](#decorating-variables-of-the-set-type). 182 183### Framework Behavior 184 185To understand the value initialization and update mechanism of the \@Prop decorated variable, it is necessary to understand the parent component and the initial render and update process of the child component that owns the \@Prop decorated variable. 186 1871. Initial render: 188 1. The execution of the parent component's **build()** function creates an instance of the child component, and the parent component provides a source for the @Prop decorated variable. 189 2. The @Prop decorated variable is initialized. 190 1912. Update: 192 1. When the @Prop decorated variable is modified locally, the change does not propagate back to its parent component. 193 2. When the data source of the parent component is updated, the \@Prop decorated variable in the child component is reset, and its local value changes are overwritten. 194 195> **NOTE** 196> 197> The update of an \@Prop decorated variable relies on the re-rendering of the owning custom component. As such, when the application is in the background, the \@Prop decorated variable cannot be updated. In this case, use \@Link instead. 198 199 200## Usage Scenarios 201 202 203### Simple Type Sync from @State of the Parent Component to @Prop of the Child Component 204 205 206In this example, the \@Prop decorated **count** variable in the **CountDownComponent** child component is initialized from the \@State decorated **countDownStartValue** variable in the **ParentComponent**. When **Try again** is touched, the value of the **count** variable is modified, but the change remains within the **CountDownComponent** and does not affect the **ParentComponent**. 207 208 209Updating **countDownStartValue** in the **ParentComponent** will update the value of the @Prop decorated **count**. 210 211 212 213```ts 214@Component 215struct CountDownComponent { 216 @Prop count: number = 0; 217 costOfOneAttempt: number = 1; 218 219 build() { 220 Column() { 221 if (this.count > 0) { 222 Text(`You have ${this.count} Nuggets left`) 223 } else { 224 Text('Game over!') 225 } 226 // Changes to the @Prop decorated variables are not synchronized to the parent component. 227 Button(`Try again`).onClick(() => { 228 this.count -= this.costOfOneAttempt; 229 }) 230 } 231 } 232} 233 234@Entry 235@Component 236struct ParentComponent { 237 @State countDownStartValue: number = 10; 238 239 build() { 240 Column() { 241 Text(`Grant ${this.countDownStartValue} nuggets to play.`) 242 // Changes to the data source provided by the parent component are synchronized to the child component. 243 Button(`+1 - Nuggets in New Game`).onClick(() => { 244 this.countDownStartValue += 1; 245 }) 246 // Updating the parent component will also update the child component. 247 Button(`-1 - Nuggets in New Game`).onClick(() => { 248 this.countDownStartValue -= 1; 249 }) 250 251 CountDownComponent({ count: this.countDownStartValue, costOfOneAttempt: 2 }) 252 } 253 } 254} 255``` 256 257 258In the preceding example: 259 260 2611. On initial render, when the **CountDownComponent** child component is created, its @Prop decorated **count** variable is initialized from the \@State decorated **countDownStartValue** variable in the **ParentComponent**. 262 2632. When the "+1" or "-1" button is touched, the @State decorated **countDownStartValue** of the **ParentComponent** changes. This will cause the **ParentComponent** to re-render. At the minimum, the **CountDownComponent** will be updated because of the change in the **count** variable value. 264 2653. Because of the change in the **count** variable value, the **CountDownComponent** child component will re-render. At a minimum, the **if** statement's condition (**this.counter> 0**) is evaluated, and the **Text** child component inside the **if** statement would be updated. 266 2674. When **Try again** in the **CountDownComponent** child component is touched, the value of the **count** variable is modified, but the change remains within the child component and does not affect the **countDownStartValue** in the parent component. 268 2695. Updating **countDownStartValue** will overwrite the local value changes of the @Prop decorated **count** in the **CountDownComponent** child component. 270 271 272### Simple Type @Prop Synced from @State Array Item in Parent Component 273 274 275The \@State decorated array an array item in the parent component can be used as data source to initialize and update a @Prop decorated variable. In the following example, the \@State decorated array **arr** in the parent component **Index** initializes the \@Prop decorated **value** variable in the child component **Child**. 276 277 278 279```ts 280@Component 281struct Child { 282 @Prop value: number = 0; 283 284 build() { 285 Text(`${this.value}`) 286 .fontSize(50) 287 .onClick(() => { 288 this.value++ 289 }) 290 } 291} 292 293@Entry 294@Component 295struct Index { 296 @State arr: number[] = [1, 2, 3]; 297 298 build() { 299 Row() { 300 Column() { 301 Child({ value: this.arr[0] }) 302 Child({ value: this.arr[1] }) 303 Child({ value: this.arr[2] }) 304 305 Divider().height(5) 306 307 ForEach(this.arr, 308 (item: number) => { 309 Child({ value: item }) 310 }, 311 (item: number) => item.toString() 312 ) 313 Text('replace entire arr') 314 .fontSize(50) 315 .onClick(() => { 316 // Both arrays contain item "3". 317 this.arr = this.arr[0] == 1 ? [3, 4, 5] : [1, 2, 3]; 318 }) 319 } 320 } 321 } 322} 323``` 324 325 326Initial render creates six instances of the **Child** component. Each \@Prop decorated variable is initialized with a copy of an array item. The **onclick** event handler of the **Child** component changes the local variable value. 327 328 329Click **1** six times, 2 five times, and **3** four times on the page. The local values of all variables are then changed to **7**. 330 331 332 333``` 3347 3357 3367 337---- 3387 3397 3407 341``` 342 343 344After **replace entire arr** is clicked, the following information is displayed: 345 346 347 348``` 3493 3504 3515 352---- 3537 3544 3555 356``` 357 358 359- Changes made in the **Child** component are not synchronized to the parent component **Index**. Therefore, even if the values of the six instances of the **Child** component are **7**, the value of **this.arr** in the **Index** component is still **[1,2,3]**. 360 361- After **replace entire arr** is clicked, if **this.arr[0] == 1** is true, **this.arr** is set to **[3, 4, 5]**. 362 363- Because **this.arr[0]** has been changed, the **Child({value: this.arr[0]})** component synchronizes the update of **this.arr[0]** to the instance's \@Prop decorated variable. The same happens for **Child({value: this.arr[1]})** and **Child({value: this.arr[2]})**. 364 365 366- The change of **this.arr** causes **ForEach** to update: According to the diff algorithm, the array item with the ID **3** is retained in this update, array items with IDs **1** and **2** are deleted, and array items with IDs **4** and **5** are added. The array before and after the update is **[1, 2, 3]** and **[3, 4, 5]**, respectively. This implies that the **Child** instance generated for item **3** is moved to the first place, but not updated. In this case, the component value corresponding to **3** is **7**, and the final render result of **ForEach** is **7**, **4**, and **5**. 367 368 369### Class Object Type @Prop Synced from @State Class Object Property in Parent Component 370 371In a library with one book and two readers, each reader can mark the book as read, and the marking does not affect the other reader. Technically speaking, local changes to the \@Prop decorated **book** object do not sync back to the @State decorated **book** in the **Library** component. 372 373In this example, the \@Observed decorator can be applied to the **book** class, but it is not mandatory. It is only needed for nested structures. This will be further explained in [Class Type @Prop Synced from @State Array Item in Parent Component](#class-type-prop-synced-from-state-array-item-in-parent-component). 374 375 376```ts 377class Book { 378 public title: string; 379 public pages: number; 380 public readIt: boolean = false; 381 382 constructor(title: string, pages: number) { 383 this.title = title; 384 this.pages = pages; 385 } 386} 387 388@Component 389struct ReaderComp { 390 @Prop book: Book = new Book("", 0); 391 392 build() { 393 Row() { 394 Text(this.book.title) 395 Text(`...has${this.book.pages} pages!`) 396 Text(`...${this.book.readIt ? "I have read" : 'I have not read it'}`) 397 .onClick(() => this.book.readIt = true) 398 } 399 } 400} 401 402@Entry 403@Component 404struct Library { 405 @State book: Book = new Book('100 secrets of C++', 765); 406 407 build() { 408 Column() { 409 ReaderComp({ book: this.book }) 410 ReaderComp({ book: this.book }) 411 } 412 } 413} 414``` 415 416### Class Type @Prop Synced from @State Array Item in Parent Component 417 418In the following example, a property of the **Book** object in the \@State decorated **allBooks** array is changed, but the system does not respond when **Mark read for everyone** is clicked. This is because the property is nested at the second layer, and the \@State decorator can observe only properties at the first layer. Therefore, the framework does not update **ReaderComp**. 419 420```ts 421let nextId: number = 1; 422 423// @Observed 424class Book { 425 public id: number; 426 public title: string; 427 public pages: number; 428 public readIt: boolean = false; 429 430 constructor(title: string, pages: number) { 431 this.id = nextId++; 432 this.title = title; 433 this.pages = pages; 434 } 435} 436 437@Component 438struct ReaderComp { 439 @Prop book: Book = new Book("", 1); 440 441 build() { 442 Row() { 443 Text(` ${this.book ? this.book.title : "Book is undefined"}`).fontColor('#e6000000') 444 Text(` has ${this.book ? this.book.pages : "Book is undefined"} pages!`).fontColor('#e6000000') 445 Text(` ${this.book ? this.book.readIt ? "I have read" : 'I have not read it' : "Book is undefined"}`).fontColor('#e6000000') 446 .onClick(() => this.book.readIt = true) 447 } 448 } 449} 450 451@Entry 452@Component 453struct Library { 454 @State allBooks: Book[] = [new Book("C#", 765), new Book("JS", 652), new Book("TS", 765)]; 455 456 build() { 457 Column() { 458 Text('library`s all time favorite') 459 .width(312) 460 .height(40) 461 .backgroundColor('#0d000000') 462 .borderRadius(20) 463 .margin(12) 464 .padding({ left: 20 }) 465 .fontColor('#e6000000') 466 ReaderComp({ book: this.allBooks[2] }) 467 .backgroundColor('#0d000000') 468 .width(312) 469 .height(40) 470 .padding({ left: 20, top: 10 }) 471 .borderRadius(20) 472 .colorBlend('#e6000000') 473 Divider() 474 Text('Books on loan to a reader') 475 .width(312) 476 .height(40) 477 .backgroundColor('#0d000000') 478 .borderRadius(20) 479 .margin(12) 480 .padding({ left: 20 }) 481 .fontColor('#e6000000') 482 ForEach(this.allBooks, (book: Book) => { 483 ReaderComp({ book: book }) 484 .margin(12) 485 .width(312) 486 .height(40) 487 .padding({ left: 20, top: 10 }) 488 .backgroundColor('#0d000000') 489 .borderRadius(20) 490 }, 491 (book: Book) => book.id.toString()) 492 Button('Add new') 493 .width(312) 494 .height(40) 495 .margin(12) 496 .fontColor('#FFFFFF 90%') 497 .onClick(() => { 498 this.allBooks.push(new Book("JA", 512)); 499 }) 500 Button('Remove first book') 501 .width(312) 502 .height(40) 503 .margin(12) 504 .fontColor('#FFFFFF 90%') 505 .onClick(() => { 506 if (this.allBooks.length > 0){ 507 this.allBooks.shift(); 508 } else { 509 console.log("length <= 0") 510 } 511 }) 512 Button("Mark read for everyone") 513 .width(312) 514 .height(40) 515 .margin(12) 516 .fontColor('#FFFFFF 90%') 517 .onClick(() => { 518 this.allBooks.forEach((book) => book.readIt = true) 519 }) 520 } 521 } 522} 523``` 524 525 To observe the property of the **Book** object, you must use \@Observed to decorate the **Book** class. Note that the \@Prop decorated state variable in the child component is synchronized from the data source of the parent component in uni-directional manner. This means that, the changes of the \@Prop decorated **book** in **ReaderComp** are not synchronized to the parent **library** component. The parent component triggers UI re-rendering only when the value is updated (compared with the last state). 526 527```ts 528@Observed 529class Book { 530 public id: number; 531 public title: string; 532 public pages: number; 533 public readIt: boolean = false; 534 535 constructor(title: string, pages: number) { 536 this.id = nextId++; 537 this.title = title; 538 this.pages = pages; 539 } 540} 541``` 542 543All instances of the \@Observed decorated class are wrapped with an opaque proxy object. This proxy can detect all property changes inside the wrapped object. If any property change happens, the proxy notifies the \@Prop, and the \@Prop value will be updated. 544 545 546 547### Simple Type @Prop with Local Initialization and No Sync from Parent Component 548 549To enable an \@Component decorated component to be reusable, \@Prop allows for optional local initialization. This makes the synchronization with a variable in the parent component a choice, rather than mandatory. Providing a data source in the parent component is optional only when local initialization is provided for the \@Prop decorated variable. 550 551The following example includes two @Prop decorated variables in the child component. 552 553- The @Prop decorated variable **customCounter** has no local initialization, and therefore it requires a synchronization source in its parent component. When the source value changes, the @Prop decorated variable is updated. 554 555- The @Prop decorated variable **customCounter2** has local initialization. In this case, specifying a synchronization source in the parent component is allowed but not mandatory. 556 557 558```ts 559@Component 560struct MyComponent { 561 @Prop customCounter: number; 562 @Prop customCounter2: number = 5; 563 564 build() { 565 Column() { 566 Row() { 567 Text(`From Main: ${this.customCounter}`).fontColor('#ff6b6565').margin({ left: -110, top: 12 }) 568 } 569 570 Row() { 571 Button('Click to change locally !') 572 .width(288) 573 .height(40) 574 .margin({ left: 30, top: 12 }) 575 .fontColor('#FFFFFF, 90%') 576 .onClick(() => { 577 this.customCounter2++ 578 }) 579 } 580 581 Row() { 582 Text(`Custom Local: ${this.customCounter2}`).fontColor('#ff6b6565').margin({ left: -110, top: 12 }) 583 } 584 } 585 } 586} 587 588@Entry 589@Component 590struct MainProgram { 591 @State mainCounter: number = 10; 592 593 build() { 594 Column() { 595 Row() { 596 Column() { 597 // customCounter must be initialized from the parent component due to lack of local initialization. Here, customCounter2 does not need to be initialized. 598 MyComponent({ customCounter: this.mainCounter }) 599 // customCounter2 of the child component can also be initialized from the parent component. The value from the parent component overwrites the locally assigned value of customCounter2 during initialization. 600 MyComponent({ customCounter: this.mainCounter, customCounter2: this.mainCounter }) 601 } 602 } 603 604 Row() { 605 Column() { 606 Button('Click to change number') 607 .width(288) 608 .height(40) 609 .margin({ left: 30, top: 12 }) 610 .fontColor('#FFFFFF, 90%') 611 .onClick(() => { 612 this.mainCounter++ 613 }) 614 } 615 } 616 } 617 } 618} 619``` 620 621 622 623### \@Prop Nesting Scenario 624 625In nesting scenario, each layer must be decorated with @Observed, and each layer must be received by @Prop. In this way, changes can be observed. 626 627```ts 628// The following is the data structure of a nested class object. 629@Observed 630class ClassA { 631 public title: string; 632 633 constructor(title: string) { 634 this.title = title; 635 } 636} 637 638@Observed 639class ClassB { 640 public name: string; 641 public a: ClassA; 642 643 constructor(name: string, a: ClassA) { 644 this.name = name; 645 this.a = a; 646 } 647} 648``` 649 650The following component hierarchy presents a data structure of nested @Prop. 651 652```ts 653@Entry 654@Component 655struct Parent { 656 @State votes: ClassB = new ClassB('Hello', new ClassA('world')) 657 658 build() { 659 Column() { 660 Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center }) { 661 Button('change ClassB name') 662 .width(312) 663 .height(40) 664 .margin(12) 665 .fontColor('#FFFFFF, 90%') 666 .onClick(() => { 667 this.votes.name = "aaaaa" 668 }) 669 Button('change ClassA title') 670 .width(312) 671 .height(40) 672 .margin(12) 673 .fontColor('#FFFFFF, 90%') 674 .onClick(() => { 675 this.votes.a.title = "wwwww" 676 }) 677 Text(this.votes.name) 678 .fontSize(16) 679 .margin(12) 680 .width(312) 681 .height(40) 682 .backgroundColor('#ededed') 683 .borderRadius(20) 684 .textAlign(TextAlign.Center) 685 .fontColor('#e6000000') 686 .onClick(() => { 687 this.votes.name = 'Bye' 688 }) 689 Text(this.votes.a.title) 690 .fontSize(16) 691 .margin(12) 692 .width(312) 693 .height(40) 694 .backgroundColor('#ededed') 695 .borderRadius(20) 696 .textAlign(TextAlign.Center) 697 .onClick(() => { 698 this.votes.a.title = "openHarmony" 699 }) 700 Child1({ vote1: this.votes.a }) 701 } 702 703 } 704 705 } 706} 707 708 709@Component 710struct Child1 { 711 @Prop vote1: ClassA = new ClassA(''); 712 713 build() { 714 Column() { 715 Text(this.vote1.title) 716 .fontSize(16) 717 .margin(12) 718 .width(312) 719 .height(40) 720 .backgroundColor('#ededed') 721 .borderRadius(20) 722 .textAlign(TextAlign.Center) 723 .onClick(() => { 724 this.vote1.title = 'Bye Bye' 725 }) 726 } 727 } 728} 729``` 730 731 732 733### Decorating Variables of the Map Type 734 735> **NOTE** 736> 737> Since API version 11, \@Prop supports the Map type. 738 739In this example, the **value** variable is of the **Map<number, string>** type. When the button is clicked, the value of **message** changes, and the UI is re-rendered. 740 741```ts 742@Component 743struct Child { 744 @Prop value: Map<number, string> = new Map([[0, "a"], [1, "b"], [3, "c"]]) 745 746 build() { 747 Column() { 748 ForEach(Array.from(this.value.entries()), (item: [number, string]) => { 749 Text(`${item[0]}`).fontSize(30) 750 Text(`${item[1]}`).fontSize(30) 751 Divider() 752 }) 753 Button('child init map').onClick(() => { 754 this.value = new Map([[0, "a"], [1, "b"], [3, "c"]]) 755 }) 756 Button('child set new one').onClick(() => { 757 this.value.set(4, "d") 758 }) 759 Button('child clear').onClick(() => { 760 this.value.clear() 761 }) 762 Button('child replace the first one').onClick(() => { 763 this.value.set(0, "aa") 764 }) 765 Button('child delete the first one').onClick(() => { 766 this.value.delete(0) 767 }) 768 } 769 } 770} 771 772 773@Entry 774@Component 775struct MapSample2 { 776 @State message: Map<number, string> = new Map([[0, "a"], [1, "b"], [3, "c"]]) 777 778 build() { 779 Row() { 780 Column() { 781 Child({ value: this.message }) 782 } 783 .width('100%') 784 } 785 .height('100%') 786 } 787} 788``` 789 790### Decorating Variables of the Set Type 791 792> **NOTE** 793> 794> Since API version 11, \@Prop supports the Set type. 795 796In this example, the **message** variable is of the **Set\<number\>** type. When the button is clicked, the value of **message** changes, and the UI is re-rendered. 797 798```ts 799@Component 800struct Child { 801 @Prop message: Set<number> = new Set([0, 1, 2, 3, 4]) 802 803 build() { 804 Column() { 805 ForEach(Array.from(this.message.entries()), (item: [number, string]) => { 806 Text(`${item[0]}`).fontSize(30) 807 Divider() 808 }) 809 Button('init set').onClick(() => { 810 this.message = new Set([0, 1, 2, 3, 4]) 811 }) 812 Button('set new one').onClick(() => { 813 this.message.add(5) 814 }) 815 Button('clear').onClick(() => { 816 this.message.clear() 817 }) 818 Button('delete the first one').onClick(() => { 819 this.message.delete(0) 820 }) 821 } 822 .width('100%') 823 } 824} 825 826 827@Entry 828@Component 829struct SetSample11 { 830 @State message: Set<number> = new Set([0, 1, 2, 3, 4]) 831 832 build() { 833 Row() { 834 Column() { 835 Child({ message: this.message }) 836 } 837 .width('100%') 838 } 839 .height('100%') 840 } 841} 842``` 843 844## Union Type @Prop 845 846@Prop supports **undefined**, **null**, and union types. In the following example, the type of **animal** is **Animals | undefined**. If the property or type of **animal** is changed when the button in the parent component **Zoo** is clicked, the change will be synced to the child component. 847 848```ts 849class Animals { 850 public name: string; 851 852 constructor(name: string) { 853 this.name = name; 854 } 855} 856 857@Component 858struct Child { 859 @Prop animal: Animals | undefined; 860 861 build() { 862 Column() { 863 Text(`Child's animal is ${this.animal instanceof Animals ? this.animal.name : 'undefined'}`).fontSize(30) 864 865 Button('Child change animals into tigers') 866 .onClick(() => { 867 // Assign the value of an instance of Animals. 868 this.animal = new Animals("Tiger") 869 }) 870 871 Button('Child change animal to undefined') 872 .onClick(() => { 873 // Assign the value undefined. 874 this.animal = undefined 875 }) 876 877 }.width('100%') 878 } 879} 880 881@Entry 882@Component 883struct Zoo { 884 @State animal: Animals | undefined = new Animals("lion"); 885 886 build() { 887 Column() { 888 Text(`Parents' animals are ${this.animal instanceof Animals ? this.animal.name : 'undefined'}`).fontSize(30) 889 890 Child({animal: this.animal}) 891 892 Button('Parents change animals into dogs') 893 .onClick(() => { 894 // Determine the animal type and update the property. 895 if (this.animal instanceof Animals) { 896 this.animal.name = "Dog" 897 } else { 898 console.info('num is undefined, cannot change property') 899 } 900 }) 901 902 Button('Parents change animal to undefined') 903 .onClick(() => { 904 // Assign the value undefined. 905 this.animal = undefined 906 }) 907 } 908 } 909} 910``` 911 912 913## FAQs 914 915### \@Prop Decorated State Variable Not Initialized 916 917The \@Prop decorated state variable must be initialized. If not initialized locally, the variable must be initialized from the parent component. If it has been initialized locally, initialization from the parent component is optional. 918 919[Incorrect Example] 920 921```ts 922@Observed 923class Commodity { 924 public price: number = 0; 925 926 constructor(price: number) { 927 this.price = price; 928 } 929} 930 931@Component 932struct PropChild { 933 @Prop fruit: Commodity; // The state variable is not initialized locally. 934 935 build() { 936 Text(`PropChild fruit ${this.fruit.price}`) 937 .onClick(() => { 938 this.fruit.price += 1; 939 }) 940 } 941} 942 943@Entry 944@Component 945struct Parent { 946 @State fruit: Commodity[] = [new Commodity(1)]; 947 948 build() { 949 Column() { 950 Text(`Parent fruit ${this.fruit[0].price}`) 951 .onClick(() => { 952 this.fruit[0].price += 1; 953 }) 954 955 // The @Prop state variable is not initialized locally, nor initialized from the parent component. 956 PropChild() 957 } 958 } 959} 960``` 961 962[Correct Example] 963 964```ts 965@Observed 966class Commodity { 967 public price: number = 0; 968 969 constructor(price: number) { 970 this.price = price; 971 } 972} 973 974@Component 975struct PropChild1 { 976 @Prop fruit: Commodity; // The state variable is not initialized locally. 977 978 build() { 979 Text(`PropChild1 fruit ${this.fruit.price}`) 980 .onClick(() => { 981 this.fruit.price += 1; 982 }) 983 } 984} 985 986@Component 987struct PropChild2 { 988 @Prop fruit: Commodity = new Commodity(1); // The state variable is initialized locally. 989 990 build() { 991 Text(`PropChild2 fruit ${this.fruit.price}`) 992 .onClick(() => { 993 this.fruit.price += 1; 994 }) 995 } 996} 997 998@Entry 999@Component 1000struct Parent { 1001 @State fruit: Commodity[] = [new Commodity(1)]; 1002 1003 build() { 1004 Column() { 1005 Text(`Parent fruit ${this.fruit[0].price}`) 1006 .onClick(() => { 1007 this.fruit[0].price += 1; 1008 }) 1009 1010 // @PropChild1 is not initialized locally and must be initialized from the parent component. 1011 PropChild1({ fruit: this.fruit[0] }) 1012 // @PropChild2 is initialized locally. In this case, initialization from the parent component is optional. 1013 PropChild2() 1014 PropChild2({ fruit: this.fruit[0] }) 1015 } 1016 } 1017} 1018``` 1019 1020### Using the a.b(this.object) Format Fails to Trigger UI Re-render 1021 1022In the **build** method, when the variable decorated by @Prop is of the object type and is called using the **a.b(this.object)** format, the native object of **this.object** is passed in the b method. If the property of **this.object** is changed, the UI cannot be re-rendered. In the following example, when the static method **Score.changeScore1** or **this.changeScore2** is used to change **this.score.value** in the custom component **Child**, the UI is not re-rendered. 1023 1024[Incorrect Example] 1025 1026```ts 1027class Score { 1028 value: number; 1029 constructor(value: number) { 1030 this.value = value; 1031 } 1032 1033 static changeScore1(param1:Score) { 1034 param1.value += 1; 1035 } 1036} 1037 1038@Entry 1039@Component 1040struct Parent { 1041 @State score: Score = new Score(1); 1042 1043 build() { 1044 Column({space:8}) { 1045 Text(`The value in Parent is ${this.score.value}.`) 1046 .fontSize(30) 1047 .fontColor(Color.Red) 1048 Child({ score: this.score }) 1049 } 1050 .width('100%') 1051 .height('100%') 1052 } 1053} 1054 1055@Component 1056struct Child { 1057 @Prop score: Score; 1058 1059 changeScore2(param2:Score) { 1060 param2.value += 2; 1061 } 1062 1063 build() { 1064 Column({space:8}) { 1065 Text(`The value in Child is ${this.score.value}.`) 1066 .fontSize(30) 1067 Button(`changeScore1`) 1068 .onClick(()=>{ 1069 // The UI cannot be re-rendered using a static method. 1070 Score.changeScore1(this.score); 1071 }) 1072 Button(`changeScore2`) 1073 .onClick(()=>{ 1074 // The UI cannot be re-rendered using this. 1075 this.changeScore2(this.score); 1076 }) 1077 } 1078 } 1079} 1080``` 1081 1082You can add a proxy for **this.score** to re-render the UI by assigning a value to the variable and then calling the variable. 1083 1084[Correct Example] 1085 1086```ts 1087class Score { 1088 value: number; 1089 constructor(value: number) { 1090 this.value = value; 1091 } 1092 1093 static changeScore1(score:Score) { 1094 score.value += 1; 1095 } 1096} 1097 1098@Entry 1099@Component 1100struct Parent { 1101 @State score: Score = new Score(1); 1102 1103 build() { 1104 Column({space:8}) { 1105 Text(`The value in Parent is ${this.score.value}.`) 1106 .fontSize(30) 1107 .fontColor(Color.Red) 1108 Child({ score: this.score }) 1109 } 1110 .width('100%') 1111 .height('100%') 1112 } 1113} 1114 1115@Component 1116struct Child { 1117 @Prop score: Score; 1118 1119 changeScore2(score:Score) { 1120 score.value += 2; 1121 } 1122 1123 build() { 1124 Column({space:8}) { 1125 Text(`The value in Child is ${this.score.value}.`) 1126 .fontSize(30) 1127 Button(`changeScore1`) 1128 .onClick(()=>{ 1129 // Add a proxy by assigning a value. 1130 let score1 = this.score; 1131 Score.changeScore1(score1); 1132 }) 1133 Button(`changeScore2`) 1134 .onClick(()=>{ 1135 // Add a proxy by assigning a value. 1136 let score2 = this.score; 1137 this.changeScore2(score2); 1138 }) 1139 } 1140 } 1141} 1142```