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