• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# \@Prop: One-Way Synchronization from Parent to Child Components
2
3
4An \@Prop decorated variable can create one-way synchronization with a variable of its parent component. \@Prop decorated variables are mutable, but changes are not 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 an \@Prop decorated variable, the value synchronization is uni-directional from the parent component to the owning component.
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 that data source changes, the @Prop decorated variable gets updated, and any locally made changes are overwritten.
19
20
21## Rules of Use
22
23| \@Prop Decorator| Description                                      |
24| ----------- | ---------------------------------------- |
25| Decorator parameters      | None.                                       |
26| Synchronization type       | One-way: from the data source provided by the parent component to the @Prop decorated variable.|
27| Allowed variable types  | string, number, boolean, or enum type.<br>**any** is not supported. The **undefined** and **null** values are not allowed.<br>The type must be specified.<br>Negative examples:<br>CompA&nbsp;({&nbsp;aProp:&nbsp;undefined&nbsp;})<br>CompA&nbsp;({&nbsp;aProp:&nbsp;null&nbsp;})<br>The type must be the same as that of the [data source](arkts-state-management-overview.md#basic-concepts). There are three cases (\@State is used as an example of the data source):<br>- The type of the \@Prop decorated variable is the same as that of the state variable of the parent component, that is, \@Prop: S and \@State: S. For an example, see [Simple Type @Prop Synced from @State in Parent Component](#simple-type-prop-synced-from-state-in-parent-component).<br>- When the state variable of the parent component is an array, the type of the \@Prop decorated variable is the same as that of the array item of the state variable of the parent component, that is, \@Prop: S and \@State: Array\<S>. For examples, see [Simple Type @Prop Synched from @State Array Item in Parent Component](#simple-type-prop-synched-from-state-array-item-in-parent-component).<br>- When the state variable of the parent component is Object or class, the type of the \@Prop decorated variableis the same as the attribute type of the state variable of the parent component, that is, \@Prop: S and \@State: { propA: S }. For examples, see [Class Object Type @Prop Synchedd from @State Class Object Property in Parent Component](#class-object-type-prop-synchedd-from-state-class-object-property-in-parent-component).|
28| Initial value for the decorated variable  | Local initialization is allowed.                                |
29
30
31## Variable Transfer/Access Rules
32
33| Transfer/Access    | Description                                      |
34| --------- | ---------------------------------------- |
35| Initialization from the parent component  | Optional. Initialization from the parent component or local initialization can be used. An \@Prop decorated variable can be initialized from a regular variable or an \@State, \@Link, \@Prop, \@Provide, \@Consume, \@ObjectLink, \@StorageLink, \@StorageProp, \@LocalStorageLink, or \@LocalStorageProp decorated variable in its parent component.|
36| Subnode initialization | Supported; can be used to initialize a regular variable or \@State, \@Link, \@Prop, or \@Provide decorated variable in the child component.|
37| Access| Private, accessible only within the component.               |
38
39
40  **Figure 1** Initialization rule
41
42
43![en-us_image_0000001552972029](figures/en-us_image_0000001552972029.png)
44
45
46## Observed Changes and Behavior
47
48
49### Observed Changes
50
51\@Prop decorated variables can observe the following changes:
52
53- When the decorated variable is of the string, number, Boolean, or enum type, its value change can be observed.
54
55  ```ts
56  // Simple type
57  @Prop count: number;
58  // The value assignment can be observed.
59  this.count = 1;
60  ```
61
62For synchronization between \@State and \@Prop decorated variables:
63
64- The value of an \@State decorated variable in the parent component initializes an \@Prop decorated variable in the child component. The \@State decorated variable also updates the @Prop decorated variable whenever the value of the former changes.
65
66- Changes to the @Prop decorated variable do not affect the value of its source @State decorated variable.
67
68- In addition to \@State, the source can also be decorated with \@Link or \@Prop, where the mechanisms for syncing the \@Prop would be the same.
69
70- The type of the source and the @Prop decorated variable must be the same.
71
72
73### Framework Behavior
74
75To understand the value initialization and update mechanism of the \@Prop decorated variable, it is necessary to consider the parent component and the initial render and update process of the child component that owns the \@Prop decorated variable.
76
771. Initial render:
78   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.
79   2. The @Prop decorated variable is initialized.
80
812. Update:
82   1. When the @Prop decorated variable is modified locally, the change remains local and does not propagate back to its parent component.
83   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.
84
85
86## Application Scenarios
87
88
89### Simple Type @Prop Synced from @State in Parent Component
90
91
92In 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 **CountDownComponent**.
93
94
95Updating **countDownStartValue** in the **ParentComponent** will update the value of the @Prop decorated **count**.
96
97
98
99```ts
100@Component
101struct CountDownComponent {
102  @Prop count: number;
103  costOfOneAttempt: number = 1;
104
105  build() {
106    Column() {
107      if (this.count > 0) {
108        Text(`You have ${this.count} Nuggets left`)
109      } else {
110        Text('Game over!')
111      }
112      // Changes to the @Prop decorated variables are not synchronized to the parent component.
113      Button(`Try again`).onClick(() => {
114        this.count -= this.costOfOneAttempt;
115      })
116    }
117  }
118}
119
120@Entry
121@Component
122struct ParentComponent {
123  @State countDownStartValue: number = 10;
124
125  build() {
126    Column() {
127      Text(`Grant ${this.countDownStartValue} nuggets to play.`)
128      // Changes to the data source provided by the parent component are synchronized to the child component.
129      Button(`+1 - Nuggets in New Game`).onClick(() => {
130        this.countDownStartValue += 1;
131      })
132      // Updating the parent component will also update the child component.
133      Button(`-1  - Nuggets in New Game`).onClick(() => {
134        this.countDownStartValue -= 1;
135      })
136
137      CountDownComponent({ count: this.countDownStartValue, costOfOneAttempt: 2 })
138    }
139  }
140}
141```
142
143
144In the preceding example:
145
146
1471. 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**.
148
1492. 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 minumum, the **CountDownComponent** will be updated because of the changed **count** variable value.
150
1513. Because of the changed **count** variable value, the **CountDownComponent** child component will re-render. At a minimum, the **if** statement's conditions (**this.counter> 0**) is-evaluated and the **\<Text>** child component inside the **if** would be updated.
152
1534. 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 parenet component.
154
1555. Updating **countDownStartValue** will overwrite the local value changes of the @Prop decorated **count** in the **CountDownComponent** child component.
156
157
158### Simple Type @Prop Synched from @State Array Item in Parent Component
159
160
161The \@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**.
162
163
164
165```ts
166@Component
167struct Child {
168  @Prop value: number;
169
170  build() {
171    Text(`${this.value}`)
172      .fontSize(50)
173      .onClick(()=>{this.value++})
174  }
175}
176
177@Entry
178@Component
179struct Index {
180  @State arr: number[] = [1,2,3];
181
182  build() {
183    Row() {
184      Column() {
185        Child({value: this.arr[0]})
186        Child({value: this.arr[1]})
187        Child({value: this.arr[2]})
188
189        Divider().height(5)
190
191        ForEach(this.arr,
192          item => {
193            Child({value: item})
194          },
195          item => item.toString()
196        )
197        Text('replace entire arr')
198        .fontSize(50)
199        .onClick(()=>{
200          // Both arrays contain item "3".
201          this.arr = this.arr[0] == 1 ? [3,4,5] : [1,2,3];
202        })
203      }
204    }
205  }
206}
207```
208
209
210Initial 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.
211
212
213Assume that we clicked so many times that all local values be '7'.
214
215
216
217```
2187
2197
2207
221----
2227
2237
2247
225```
226
227
228After **replace entire arr** is clicked, the following information is displayed:
229
230
231
232```
2333
2344
2355
236----
2377
2384
2395
240```
241
242
243- Changes made in the **Child** component are not synchronized to the parent component **Index**. Therefore, even if the values of the six intances of the **Child** component are 7, the value of **this.arr** in the **Index** component is still **[1,2,3]**.
244
245- After **replace entire arr** is clicked, if **this.arr[0] == 1** is true, **this.arr** is set to **[3, 4, 5]**.
246
247- 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]})**.
248
249
250- The change of **this.arr** causes **ForEach** to update: 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** will be 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**.
251
252
253### Class Object Type @Prop Synchedd from @State Class Object Property in Parent Component
254
255In a library with one book and two users, each user can mark the book as read, but this does not affect the other user reader. Technically speaking, local changes to the \@Prop decorated **book** object do not sync back to the @State decorated **book** in the **Library** component.
256
257
258```ts
259class Book {
260  public title: string;
261  public pages: number;
262  public readIt: boolean = false;
263
264  constructor(title: string, pages: number) {
265    this.title = title;
266    this.pages = pages;
267  }
268}
269
270@Component
271struct ReaderComp {
272  @Prop title: string;
273  @Prop readIt: boolean;
274
275  build() {
276    Row() {
277      Text(this.title)
278      Text(`... ${this.readIt ? 'I have read' : 'I have not read it'}`)
279        .onClick(() => this.readIt = true)
280    }
281  }
282}
283
284@Entry
285@Component
286struct Library {
287  @State book: Book = new Book('100 secrets of C++', 765);
288
289  build() {
290    Column() {
291      ReaderComp({ title: this.book.title, readIt: this.book.readIt })
292      ReaderComp({ title: this.book.title, readIt: this.book.readIt })
293    }
294  }
295}
296```
297
298
299### Simple Type @Prop with Local Initialization and No Sync from Parent Parent
300
301To 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.
302
303The following example includes two @Prop decorated variables in child component.
304
305- 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 udpated.
306
307- The @Prop decorated variable **customCounter2** has local initialization. In this case, specifying a synchronization source in the parent component is allowed but not mandatory.
308
309
310```ts
311@Component
312struct MyComponent {
313  @Prop customCounter: number;
314  @Prop customCounter2: number = 5;
315
316  build() {
317    Column() {
318      Row() {
319        Text(`From Main: ${this.customCounter}`).width(90).height(40).fontColor('#FF0010')
320      }
321
322      Row() {
323        Button('Click to change locally !').width(480).height(60).margin({ top: 10 })
324          .onClick(() => {
325            this.customCounter2++
326          })
327      }.height(100).width(480)
328
329      Row() {
330        Text(`Custom Local: ${this.customCounter2}`).width(90).height(40).fontColor('#FF0010')
331      }
332    }
333  }
334}
335
336@Entry
337@Component
338struct MainProgram {
339  @State mainCounter: number = 10;
340
341  build() {
342    Column() {
343      Row() {
344        Column() {
345          Button('Click to change number').width(480).height(60).margin({ top: 10, bottom: 10 })
346            .onClick(() => {
347              this.mainCounter++
348            })
349        }
350      }
351
352      Row() {
353        Column()
354          // customCounter must be initialized from the parent component due to lack of local initialization. Here, customCounter2 does not need to be initialized.
355          MyComponent({ customCounter: this.mainCounter })
356          // 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.
357          MyComponent({ customCounter: this.mainCounter, customCounter2: this.mainCounter })
358        }.width('40%')
359    }
360  }
361}
362```
363