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