• 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
6
7> **NOTE**
8>
9> Since API version 9, this decorator is supported in ArkTS widgets.
10
11
12## Overview
13
14For the \@Prop decorated variable of a child component, the change synchronization to the parent component is uni-directional.
15
16- An \@Prop variable is allowed to be modified locally, but the change does not propagate back to its parent component.
17
18- 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.
19
20
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>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| The union types defined by the ArkUI framework, including Length, ResourceStr, and ResourceColor, are supported.| 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 Sync from @State of the Parent Component to @Prop of the Child Component](#simple-type-sync-from-state-of-the-parent-component-to-prop-of-the-child-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 Sync from @State Class Object Property in Parent Component](#class-object-type-sync-from-state-class-object-property-in-parent-component).|
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, @Link, @Prop, @Provide, @Consume, @ObjectLink, @StorageLink, @StorageProp, @LocalStorageLink, or @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![en-us_image_0000001552972029](figures/en-us_image_0000001552972029.png)
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, 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## Use 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 Sync 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
280
281```ts
282@Component
283struct Child {
284  @Prop value: number = 0;
285
286  build() {
287    Text(`${this.value}`)
288      .fontSize(50)
289      .onClick(() => {
290        this.value++
291      })
292  }
293}
294
295@Entry
296@Component
297struct Index {
298  @State arr: number[] = [1, 2, 3];
299
300  build() {
301    Row() {
302      Column() {
303        Child({ value: this.arr[0] })
304        Child({ value: this.arr[1] })
305        Child({ value: this.arr[2] })
306
307        Divider().height(5)
308
309        ForEach(this.arr,
310          (item: number) => {
311            Child({ value: item })
312          },
313          (item: string) => item.toString()
314        )
315        Text('replace entire arr')
316          .fontSize(50)
317          .onClick(() => {
318            // Both arrays contain item "3".
319            this.arr = this.arr[0] == 1 ? [3, 4, 5] : [1, 2, 3];
320          })
321      }
322    }
323  }
324}
325```
326
327
328Initial 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.
329
330
331Click **1** six times, 2 five times, and **3** four times on the page. The local values of all variables are then changed to **7**.
332
333
334
335```
3367
3377
3387
339----
3407
3417
3427
343```
344
345
346After **replace entire arr** is clicked, the following information is displayed:
347
348
349
350```
3513
3524
3535
354----
3557
3564
3575
358```
359
360
361- 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]**.
362
363- After **replace entire arr** is clicked, if **this.arr[0] == 1** is true, **this.arr** is set to **[3, 4, 5]**.
364
365- 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]})**.
366
367
368- 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**.
369
370
371### Class Object Type Sync from @State Class Object Property in Parent Component
372
373In 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.
374
375In 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 Sync from @State Array Item in Parent Component](#class-type-sync-from-state-array-item-in-parent-component).
376
377
378```ts
379class Book {
380  public title: string;
381  public pages: number;
382  public readIt: boolean = false;
383
384  constructor(title: string, pages: number) {
385    this.title = title;
386    this.pages = pages;
387  }
388}
389
390@Component
391struct ReaderComp {
392  @Prop book: Book = new Book("", 0);
393
394  build() {
395    Row() {
396      Text(this.book.title)
397      Text(`...has${this.book.pages} pages!`)
398      Text(`...${this.book.readIt ? "I have read" : 'I have not read it'}`)
399        .onClick(() => this.book.readIt = true)
400    }
401  }
402}
403
404@Entry
405@Component
406struct Library {
407  @State book: Book = new Book('100 secrets of C++', 765);
408
409  build() {
410    Column() {
411      ReaderComp({ book: this.book })
412      ReaderComp({ book: this.book })
413    }
414  }
415}
416```
417
418### Class Type Sync from @State Array Item in Parent Component
419
420In 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**.
421
422```ts
423let nextId: number = 1;
424
425// @Observed
426class Book {
427  public id: number;
428  public title: string;
429  public pages: number;
430  public readIt: boolean = false;
431
432  constructor(title: string, pages: number) {
433    this.id = nextId++;
434    this.title = title;
435    this.pages = pages;
436  }
437}
438
439@Component
440struct ReaderComp {
441  @Prop book: Book = new Book("", 1);
442
443  build() {
444    Row() {
445      Text(` ${this.book ? this.book.title : "Book is undefined"}`).fontColor('#e6000000')
446      Text(` has ${this.book ? this.book.pages : "Book is undefined"} pages!`).fontColor('#e6000000')
447      Text(` ${this.book ? this.book.readIt ? "I have read" : 'I have not read it' : "Book is undefined"}`).fontColor('#e6000000')
448        .onClick(() => this.book.readIt = true)
449    }
450  }
451}
452
453@Entry
454@Component
455struct Library {
456  @State allBooks: Book[] = [new Book("C#", 765), new Book("JS", 652), new Book("TS", 765)];
457
458  build() {
459    Column() {
460      Text('library`s all time favorite')
461        .width(312)
462        .height(40)
463        .backgroundColor('#0d000000')
464        .borderRadius(20)
465        .margin(12)
466        .padding({ left: 20 })
467        .fontColor('#e6000000')
468      ReaderComp({ book: this.allBooks[2] })
469        .backgroundColor('#0d000000')
470        .width(312)
471        .height(40)
472        .padding({ left: 20, top: 10 })
473        .borderRadius(20)
474        .colorBlend('#e6000000')
475      Divider()
476      Text('Books on loaan to a reader')
477        .width(312)
478        .height(40)
479        .backgroundColor('#0d000000')
480        .borderRadius(20)
481        .margin(12)
482        .padding({ left: 20 })
483        .fontColor('#e6000000')
484      ForEach(this.allBooks, (book: Book) => {
485        ReaderComp({ book: book })
486          .margin(12)
487          .width(312)
488          .height(40)
489          .padding({ left: 20, top: 10 })
490          .backgroundColor('#0d000000')
491          .borderRadius(20)
492      },
493        (book: Book) => book.id.toString())
494      Button('Add new')
495        .width(312)
496        .height(40)
497        .margin(12)
498        .fontColor('#FFFFFF 90%')
499        .onClick(() => {
500          this.allBooks.push(new Book("JA", 512));
501        })
502      Button('Remove first book')
503        .width(312)
504        .height(40)
505        .margin(12)
506        .fontColor('#FFFFFF 90%')
507        .onClick(() => {
508          if (this.allBooks.length > 0){
509            this.allBooks.shift();
510          } else {
511            console.log("length <= 0")
512          }
513        })
514      Button("Mark read for everyone")
515        .width(312)
516        .height(40)
517        .margin(12)
518        .fontColor('#FFFFFF 90%')
519        .onClick(() => {
520          this.allBooks.forEach((book) => book.readIt = true)
521        })
522    }
523  }
524}
525```
526
527 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).
528
529```ts
530@Observed
531class Book {
532  public id: number;
533  public title: string;
534  public pages: number;
535  public readIt: boolean = false;
536
537  constructor(title: string, pages: number) {
538    this.id = nextId++;
539    this.title = title;
540    this.pages = pages;
541  }
542}
543```
544
545All 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.
546
547![Video-prop-UsageScenario-one](figures/Video-prop-UsageScenario-one.gif)
548
549### Simple Type @Prop with Local Initialization and No Sync from Parent Component
550
551To 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.
552
553The following example includes two @Prop decorated variables in the child component.
554
555- 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.
556
557- The @Prop decorated variable **customCounter2** has local initialization. In this case, specifying a synchronization source in the parent component is allowed but not mandatory.
558
559
560```ts
561@Component
562struct MyComponent {
563  @Prop customCounter: number;
564  @Prop customCounter2: number = 5;
565
566  build() {
567    Column() {
568      Row() {
569        Text(`From Main: ${this.customCounter}`).fontColor('#ff6b6565').margin({ left: -110, top: 12 })
570      }
571
572      Row() {
573        Button('Click to change locally !')
574          .width(288)
575          .height(40)
576          .margin({ left: 30, top: 12 })
577          .fontColor('#FFFFFF, 90%')
578          .onClick(() => {
579            this.customCounter2++
580          })
581      }
582
583      Row() {
584        Text(`Custom Local: ${this.customCounter2}`).fontColor('#ff6b6565').margin({ left: -110, top: 12 })
585      }
586    }
587  }
588}
589
590@Entry
591@Component
592struct MainProgram {
593  @State mainCounter: number = 10;
594
595  build() {
596    Column() {
597      Row() {
598        Column() {
599          // customCounter must be initialized from the parent component due to lack of local initialization. Here, customCounter2 does not need to be initialized.
600          MyComponent({ customCounter: this.mainCounter })
601          // 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.
602          MyComponent({ customCounter: this.mainCounter, customCounter2: this.mainCounter })
603        }
604      }
605
606      Row() {
607        Column() {
608          Button('Click to change number')
609            .width(288)
610            .height(40)
611            .margin({ left: 30, top: 12 })
612            .fontColor('#FFFFFF, 90%')
613            .onClick(() => {
614              this.mainCounter++
615            })
616        }
617      }
618    }
619  }
620}
621```
622
623![Video-prop-UsageScenario-two](figures/Video-prop-UsageScenario-two.gif)
624
625### \@Prop Nesting Scenario
626
627In nesting scenario, each layer must be decorated with @Observed, and each layer must be received by @Prop. In this way, changes can be observed.
628
629```ts
630// The following is the data structure of a nested class object.
631@Observed
632class ClassA {
633  public title: string;
634
635  constructor(title: string) {
636    this.title = title;
637  }
638}
639
640@Observed
641class ClassB {
642  public name: string;
643  public a: ClassA;
644
645  constructor(name: string, a: ClassA) {
646    this.name = name;
647    this.a = a;
648  }
649}
650```
651
652The following component hierarchy presents a data structure of nested @Prop.
653
654```ts
655@Entry
656@Component
657struct Parent {
658  @State votes: ClassB = new ClassB('Hello', new ClassA('world'))
659
660  build() {
661    Column() {
662      Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center }) {
663        Button('change ClassB name')
664          .width(312)
665          .height(40)
666          .margin(12)
667          .fontColor('#FFFFFF, 90%')
668          .onClick(() => {
669            this.votes.name = "aaaaa"
670          })
671        Button('change ClassA title')
672          .width(312)
673          .height(40)
674          .margin(12)
675          .fontColor('#FFFFFF, 90%')
676          .onClick(() => {
677            this.votes.a.title = "wwwww"
678          })
679        Text(this.votes.name)
680          .fontSize(16)
681          .margin(12)
682          .width(312)
683          .height(40)
684          .backgroundColor('#ededed')
685          .borderRadius(20)
686          .textAlign(TextAlign.Center)
687          .fontColor('#e6000000')
688          .onClick(() => {
689            this.votes.name = 'Bye'
690          })
691        Text(this.votes.a.title)
692          .fontSize(16)
693          .margin(12)
694          .width(312)
695          .height(40)
696          .backgroundColor('#ededed')
697          .borderRadius(20)
698          .textAlign(TextAlign.Center)
699          .onClick(() => {
700            this.votes.a.title = "openHarmony"
701          })
702        Child1({ vote1: this.votes.a })
703      }
704
705    }
706
707  }
708}
709
710
711@Component
712struct Child1 {
713  @Prop vote1: ClassA = new ClassA('');
714
715  build() {
716    Column() {
717      Text(this.vote1.title)
718        .fontSize(16)
719        .margin(12)
720        .width(312)
721        .height(40)
722        .backgroundColor('#ededed')
723        .borderRadius(20)
724        .textAlign(TextAlign.Center)
725        .onClick(() => {
726          this.vote1.title = 'Bye Bye'
727        })
728    }
729  }
730}
731```
732
733![Video-prop-UsageScenario-three](figures/Video-prop-UsageScenario-three.gif)
734
735### Decorating Variables of the Map Type
736
737> **NOTE**
738>
739> Since API version 11, \@Prop supports the Map type.
740
741In 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.
742
743```ts
744@Component
745struct Child {
746  @Prop value: Map<number, string> = new Map([[0, "a"], [1, "b"], [3, "c"]])
747
748  build() {
749    Column() {
750      ForEach(Array.from(this.value.entries()), (item: [number, string]) => {
751        Text(`${item[0]}`).fontSize(30)
752        Text(`${item[1]}`).fontSize(30)
753        Divider()
754      })
755      Button('child init map').onClick(() => {
756        this.value = new Map([[0, "a"], [1, "b"], [3, "c"]])
757      })
758      Button('child set new one').onClick(() => {
759        this.value.set(4, "d")
760      })
761      Button('child clear').onClick(() => {
762        this.value.clear()
763      })
764      Button('child replace the first one').onClick(() => {
765        this.value.set(0, "aa")
766      })
767      Button('child delete the first one').onClick(() => {
768        this.value.delete(0)
769      })
770    }
771  }
772}
773
774
775@Entry
776@Component
777struct MapSample2 {
778  @State message: Map<number, string> = new Map([[0, "a"], [1, "b"], [3, "c"]])
779
780  build() {
781    Row() {
782      Column() {
783        Child({ value: this.message })
784      }
785      .width('100%')
786    }
787    .height('100%')
788  }
789}
790```
791
792### Decorating Variables of the Set Type
793
794> **NOTE**
795>
796> Since API version 11, \@Prop supports the Set type.
797
798In 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.
799
800```ts
801@Component
802struct Child {
803  @Prop message: Set<number> = new Set([0, 1, 2, 3, 4])
804
805  build() {
806    Column() {
807      ForEach(Array.from(this.message.entries()), (item: [number, string]) => {
808        Text(`${item[0]}`).fontSize(30)
809        Divider()
810      })
811      Button('init set').onClick(() => {
812        this.message = new Set([0, 1, 2, 3, 4])
813      })
814      Button('set new one').onClick(() => {
815        this.message.add(5)
816      })
817      Button('clear').onClick(() => {
818        this.message.clear()
819      })
820      Button('delete the first one').onClick(() => {
821        this.message.delete(0)
822      })
823    }
824    .width('100%')
825  }
826}
827
828
829@Entry
830@Component
831struct SetSample11 {
832  @State message: Set<number> = new Set([0, 1, 2, 3, 4])
833
834  build() {
835    Row() {
836      Column() {
837        Child({ message: this.message })
838      }
839      .width('100%')
840    }
841    .height('100%')
842  }
843}
844```
845
846## Union Type @Prop
847
848@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.
849
850```ts
851class Animals {
852  public name: string;
853
854  constructor(name: string) {
855    this.name = name;
856  }
857}
858
859@Component
860struct Child {
861  @Prop animal: Animals | undefined;
862
863  build() {
864    Column() {
865      Text(`Child's animal is  ${this.animal instanceof Animals ? this.animal.name : 'undefined'}`).fontSize(30)
866
867      Button('Child change animals into tigers')
868        .onClick(() => {
869          // Assign the value of an instance of Animals.
870          this.animal = new Animals("Tiger")
871        })
872
873      Button('Child change animal to undefined')
874        .onClick(() => {
875          // Assign the value undefined.
876          this.animal = undefined
877        })
878
879    }.width('100%')
880  }
881}
882
883@Entry
884@Component
885struct Zoo {
886  @State animal: Animals | undefined = new Animals("lion");
887
888  build() {
889    Column() {
890      Text(`Parents' animals are  ${this.animal instanceof Animals ? this.animal.name : 'undefined'}`).fontSize(30)
891
892      Child({animal: this.animal})
893
894      Button('Parents change animals into dogs')
895        .onClick(() => {
896          // Determine the animal type and update the property.
897          if (this.animal instanceof Animals) {
898            this.animal.name = "Dog"
899          } else {
900            console.info('num is undefined, cannot change property')
901          }
902        })
903
904      Button('Parents change animal to undefined')
905        .onClick(() => {
906          // Assign the value undefined.
907          this.animal = undefined
908        })
909    }
910  }
911}
912```
913
914
915## FAQs
916
917### \@Prop Decorated State Variable Not Initialized
918
919The \@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.
920
921[Nonexample]
922
923```ts
924@Observed
925class ClassA {
926  public c: number = 0;
927
928  constructor(c: number) {
929    this.c = c;
930  }
931}
932
933@Component
934struct PropChild {
935  @Prop testNum: ClassA; // The state variable is not initialized locally.
936
937  build() {
938    Text(`PropChild testNum ${this.testNum.c}`)
939      .onClick(() => {
940        this.testNum.c += 1;
941      })
942  }
943}
944
945@Entry
946@Component
947struct Parent {
948  @State testNum: ClassA[] = [new ClassA(1)];
949
950  build() {
951    Column() {
952      Text(`Parent testNum ${this.testNum[0].c}`)
953        .onClick(() => {
954          this.testNum[0].c += 1;
955        })
956
957      // The @Prop state variable is not initialized locally, nor initialized from the parent component.
958      PropChild1()
959    }
960  }
961}
962```
963
964[Example]
965
966```ts
967@Observed
968class ClassA {
969  public c: number = 0;
970
971  constructor(c: number) {
972    this.c = c;
973  }
974}
975
976@Component
977struct PropChild1 {
978  @Prop testNum: ClassA; // The state variable is not initialized locally.
979
980  build() {
981    Text(`PropChild1 testNum ${this.testNum.c}`)
982      .onClick(() => {
983        this.testNum.c += 1;
984      })
985  }
986}
987
988@Component
989struct PropChild2 {
990  @Prop testNum: ClassA = new ClassA(1); // The state variable is initialized locally.
991
992  build() {
993    Text(`PropChild2 testNum ${this.testNum.c}`)
994      .onClick(() => {
995        this.testNum.c += 1;
996      })
997  }
998}
999
1000@Entry
1001@Component
1002struct Parent {
1003  @State testNum: ClassA[] = [new ClassA(1)];
1004
1005  build() {
1006    Column() {
1007      Text(`Parent testNum ${this.testNum[0].c}`)
1008        .onClick(() => {
1009          this.testNum[0].c += 1;
1010        })
1011
1012      // @PropChild1 is not initialized locally and must be initialized from the parent component.
1013      PropChild1({ testNum: this.testNum[0] })
1014      // @PropChild2 is initialized locally. In this case, initialization from the parent component is optional.
1015      PropChild2()
1016      PropChild2({ testNum: this.testNum[0] })
1017    }
1018  }
1019}
1020```
1021
1022<!--no_check-->
1023