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