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