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