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