1# \@Provide and \@Consume Decorators: Two-Way Synchronization with Descendant Components
2
3
4\@Provide and \@Consume are used for two-way data synchronization with descendant components when state data needs to be transferred between multiple levels. They do not involve passing a variable from component to component multiple times.
5
6
7An \@Provide decorated state variable exists in the ancestor component and is said to be "provided" to descendent components. An \@Consume decorated state variable is used in a descendent component. It is linked to ("consumes") the provided state variable in its ancestor component.
8
9
10> **NOTE**
11>
12> These two decorators can be used in ArkTS widgets since API version 9.
13>
14> These two decorators can be used in atomic services since API version 11.
15
16## Overview
17
18\@Provide/\@Consume decorated state variables have the following features:
19
20- An \@Provide decorated state variable becomes available to all descendent components of the providing component automatically. The variable is said to be "provided" to other components. This means that you do not need to pass a variable from component to component multiple times.
21
22- A descendent component gains access to the provided state variable by decorating a variable with \@Consume. This establishes a two-way data synchronization between the provided and the consumed variable. This synchronization works in the same manner as a combination of \@State and \@Link does. The only difference is that the former allows transfer across multiple levels of the UI parent-child hierarchy.
23
24- \@Provide and \@Consume can be bound using the same variable name or variable alias. Whenever possible, use the same variable types to prevent implicit type conversion and consequently application behavior exceptions.
25
26
27```ts
28// Binding through the same variable name
29@Provide a: number = 0;
30@Consume a: number;
31
32// Binding through the same variable alias
33@Provide('a') b: number = 0;
34@Consume('a') c: number;
35```
36
37
38When \@Provide and \@Consume are bound through the same variable name or variable alias, the variables decorated by \@Provide and \@Consume are in a one-to-many relationship. A custom component, including its child components, should not contain multiple \@Provide decorated variables under the same name or alias. Otherwise, a runtime error will occur.
39
40
41## Decorator Description
42
43The rules of \@State also apply to \@Provide. The difference is that \@Provide also functions as a synchronization source for multi-layer descendants.
44
45| \@Provide Decorator| Description                                      |
46| -------------- | ---------------------------------------- |
47| Decorator parameters         | Alias: constant string, optional.<br>If the alias is specified, the variable is provided under the alias name only. If the alias is not specified, the variable is provided under the variable name.|
48| Synchronization type          | Two-way:<br>from the \@Provide decorated variable to all \@Consume decorated variables; and the other way around. The two-way synchronization behaviour is the same as that of the combination of \@State and \@Link.|
49| Allowed variable types | Object, class, string, number, Boolean, enum, and array of these types.<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.<br>The type must be specified.<br/>The type of the provided and the consumed variables must be the same.<br>For details about the scenarios of supported types, see [Observed Changes](#observed-changes).<br>**any** is not supported.<br>(Applicable to API version 11 and later versions) Union type of the preceding types, for example, **string \| number**, **string \| undefined** or **ClassA \| null**. For details, see [Support for Union Type](#support-for-union-type).<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, **@Provide a: string \| undefined = undefined** is recommended; **@Provide a: string = undefined** is not recommended. |
50| Initial value for the decorated variable | Mandatory. |
51| Support for the **allowOverride** parameter | Yes. After **allowOverride** is declared, both aliases and attribute names can be overridden. For details, see [Support for the allowOverride Parameter](#support-for-the-allowoverride-parameter). |
52
53
54| \@Consume Decorator| Description                                      |
55| -------------- | ---------------------------------------- |
56| Decorator parameters         | Alias: constant string, optional.<br>If the alias is specified, the alias name is used for matching with the \@Provide decorated variable. Otherwise, the variable name is used.|
57| Synchronization type          | Two-way: from the \@Provide decorated variable to all \@Consume decorated variables; and the other way around. The two-way synchronization behaviour is the same as that of the combination of \@State and \@Link.|
58| Allowed variable types | Object, class, string, number, Boolean, enum, and array of these types.<br>Date type.<br>The union types defined by the ArkUI framework, including Length, ResourceStr, and ResourceColor.<br/>The type must be specified.<br/>The type of the provided and the consumed variables must be the same.<br>An \@Consume decorated variable must have a matching \@Provide decorated variable with the corresponding attribute and alias on its parent or ancestor component.<br>For details about the scenarios of supported types, see [Observed Changes](#observed-changes).<br>**any** is not supported.<br>(Applicable to API version 11 and later versions) Union type of the preceding types, for example, **string \| number**, **string \| undefined** or **ClassA \| null**. For details, see [Support for Union Type](#support-for-union-type).<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, **@Consume a: string \| undefined**. |
59| Initial value for the decorated variable | Initialization of the decorated variables is forbidden. |
60
61## Variable Transfer/Access Rules
62
63
64| \@Provide Transfer/Access| Description                                      |
65| -------------- | ---------------------------------------- |
66| Initialization and update from the parent component    | Optional. An @Provide decorated variable can be initialized from a regular variable (whose change does not trigger UI refresh) or an [\@State](./arkts-state.md), [\@Link](./arkts-link.md), [\@Prop](./arkts-prop.md), \@Provide, \@Consume, [\@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.|
67| Subnode initialization      | Supported; can be used to initialize an \@State, \@Link, \@Prop, or \@Provide decorated variable in the child component.|
68| Synchronization with the parent component        | Not supported.                                      |
69| Synchronization with descendant components       | Two-way with @Consume decorated variables in descendant components.                         |
70| Access     | Private, accessible only within the component.                         |
71
72
73  **Figure 1** \@Provide initialization rule 
74
75
76![en-us_image_0000001552614217](figures/en-us_image_0000001552614217.png)
77
78
79| \@Consume Transfer/Access| Description                                      |
80| -------------- | ---------------------------------------- |
81| Initialization and update from the parent component    | Forbidden. Initialized from the \@Provide decorated variable with the same name or alias.     |
82| Subnode initialization      | Supported; can be used to initialize an \@State, \@Link, \@Prop, or \@Provide decorated variable in the child component.|
83| Synchronization with the ancestor component       | Two-way with the @Provide decorated variable in the ancestor component.                         |
84| Access     | Private, accessible only within the component.                          |
85
86
87  **Figure 2** \@Consume initialization rule 
88
89
90![en-us_image_0000001502094666](figures/en-us_image_0000001502094666.png)
91
92
93## Observed Changes and Behavior
94
95
96### Observed Changes
97
98- When the decorated variable is of the Boolean, string, or number type, its value change can be observed.
99
100- When the decorated variable is of the class or Object type, its value change and value changes of all its attributes, that is, the attributes that **Object.keys(observedObject)** returns, can be observed.
101
102- When the decorated variable is of the array type, the addition, deletion, and updates of array items can be observed.
103
104- When the decorated variable is of the Date type, the overall value assignment of the Date object can be observed, and the following APIs can be called to update Date attributes: **setFullYear**, **setMonth**, **setDate**, **setHours**, **setMinutes**, **setSeconds**, **setMilliseconds**, **setTime**, **setUTCFullYear**, **setUTCMonth**, **setUTCDate**, **setUTCHours**, **setUTCMinutes**, **setUTCSeconds**, and **setUTCMilliseconds**.
105
106```ts
107@Component
108struct CompD {
109  @Consume selectedDate: Date;
110
111  build() {
112    Column() {
113      Button(`child increase the day by 1`)
114        .onClick(() => {
115          this.selectedDate.setDate(this.selectedDate.getDate() + 1)
116        })
117      Button('child update the new date')
118        .margin(10)
119        .onClick(() => {
120          this.selectedDate = new Date('2023-09-09')
121        })
122      DatePicker({
123        start: new Date('1970-1-1'),
124        end: new Date('2100-1-1'),
125        selected: this.selectedDate
126      })
127    }
128  }
129}
130
131@Entry
132@Component
133struct CompA {
134  @Provide selectedDate: Date = new Date('2021-08-08')
135
136  build() {
137    Column() {
138      Button('parent increase the day by 1')
139        .margin(10)
140        .onClick(() => {
141          this.selectedDate.setDate(this.selectedDate.getDate() + 1)
142        })
143      Button('parent update the new date')
144        .margin(10)
145        .onClick(() => {
146          this.selectedDate = new Date('2023-07-07')
147        })
148      DatePicker({
149        start: new Date('1970-1-1'),
150        end: new Date('2100-1-1'),
151        selected: this.selectedDate
152      })
153      CompD()
154    }
155  }
156}
157```
158
159- 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).
160
161- 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).
162
163### Framework Behavior
164
1651. Initial render:
166   1. The \@Provide decorated variable is passed to all child components of the owning component in map mode.
167   2. If an \@Consume decorated variable is used in a child component, the system checks the map for a matching \@Provide decorated variable based on the variable name or alias. If no matching variable is found, the framework throws a JS error.
168   3. The process of initializing the @Consume decorated variable is similar to that of initializing the @State/@Link decorated variable. The @Consume decorated variable saves the matching @Provide decorated variable found in the map and registers itself with the @Provide decorated variable.
169
1702. When the \@Provide decorated variable is updated:
171   1. The system traverses and updates all system components (**elementid**) and state variable (\@Consume) that depend on the \@Provide decorated variable, with which the \@Consume decorated variable has registered itself on initial render.  
172   2. After the \@Consume decorated variable is updated in all owning child components, all system components (**elementId**) that depend on the \@Consume decorated variable are updated. In this way, changes to the \@Provide decorated variable are synchronized to the \@Consume decorated variable.
173
1743. When the \@Consume decorated variable is updated:
175
176   As can be learned from the initial render procedure, the \@Consume decorated variable holds an instance of \@Provide. After the \@Consume decorated variable is updated, the update method of \@Provide is called to synchronize the changes to \@Provide.
177
178![Provide_Consume_framework_behavior](figures/Provide_Consume_framework_behavior.png)
179
180
181## Application Scenarios
182
183The following example shows the two-way synchronization between \@Provide and \@Consume decorated variables. When the buttons in the **CompA** and **CompD** components are clicked, the changes to **reviewVotes** are synchronized to the **CompA** and **CompD** components.
184
185```ts
186@Component
187struct CompD {
188  // The @Consume decorated variable is bound to the @Provide decorated variable in its ancestor component CompA under the same attribute name.
189  @Consume reviewVotes: number;
190
191  build() {
192    Column() {
193      Text(`reviewVotes(${this.reviewVotes})`)
194      Button(`reviewVotes(${this.reviewVotes}), give +1`)
195        .onClick(() => this.reviewVotes += 1)
196    }
197    .width('50%')
198  }
199}
200
201@Component
202struct CompC {
203  build() {
204    Row({ space: 5 }) {
205      CompD()
206      CompD()
207    }
208  }
209}
210
211@Component
212struct CompB {
213  build() {
214    CompC()
215  }
216}
217
218@Entry
219@Component
220struct CompA {
221  // @Provide decorated variable reviewVotes is provided by the entry component CompA.
222  @Provide reviewVotes: number = 0;
223
224  build() {
225    Column() {
226      Button(`reviewVotes(${this.reviewVotes}), give +1`)
227        .onClick(() => this.reviewVotes += 1)
228      CompB()
229    }
230  }
231}
232```
233
234### Decorating Variables of the Map Type
235
236> **NOTE**
237>
238> \@Provide and \@Consume support the Map type since API version 11.
239
240In this example, the **message** variable is of the Map<number, string> type. After the button is clicked, the value of **message** changes, and the UI is re-rendered.
241
242```ts
243@Component
244struct Child {
245  @Consume message: Map<number, string>
246
247  build() {
248    Column() {
249      ForEach(Array.from(this.message.entries()), (item: [number, string]) => {
250        Text(`${item[0]}`).fontSize(30)
251        Text(`${item[1]}`).fontSize(30)
252        Divider()
253      })
254      Button('Consume init map').onClick(() => {
255        this.message = new Map([[0, "a"], [1, "b"], [3, "c"]])
256      })
257      Button('Consume set new one').onClick(() => {
258        this.message.set(4, "d")
259      })
260      Button('Consume clear').onClick(() => {
261        this.message.clear()
262      })
263      Button('Consume replace the first item').onClick(() => {
264        this.message.set(0, "aa")
265      })
266      Button('Consume delete the first item').onClick(() => {
267        this.message.delete(0)
268      })
269    }
270  }
271}
272
273
274@Entry
275@Component
276struct MapSample {
277  @Provide message: Map<number, string> = new Map([[0, "a"], [1, "b"], [3, "c"]])
278
279  build() {
280    Row() {
281      Column() {
282        Button('Provide init map').onClick(() => {
283          this.message = new Map([[0, "a"], [1, "b"], [3, "c"], [4, "d"]])
284        })
285        Child()
286      }
287      .width('100%')
288    }
289    .height('100%')
290  }
291}
292```
293
294### Decorating Variables of the Set Type
295
296> **NOTE**
297>
298> \@Provide and \@Consume support the Set type since API version 11.
299
300In this example, the **message** variable is of the Set\<number\> type. After the button is clicked, the value of **message** changes, and the UI is re-rendered.
301
302```ts
303@Component
304struct Child {
305  @Consume message: Set<number>
306
307  build() {
308    Column() {
309      ForEach(Array.from(this.message.entries()), (item: [number, string]) => {
310        Text(`${item[0]}`).fontSize(30)
311        Divider()
312      })
313      Button('Consume init set').onClick(() => {
314        this.message = new Set([0, 1, 2, 3, 4])
315      })
316      Button('Consume set new one').onClick(() => {
317        this.message.add(5)
318      })
319      Button('Consume clear').onClick(() => {
320        this.message.clear()
321      })
322      Button('Consume delete the first one').onClick(() => {
323        this.message.delete(0)
324      })
325    }
326    .width('100%')
327  }
328}
329
330
331@Entry
332@Component
333struct SetSample {
334  @Provide message: Set<number> = new Set([0, 1, 2, 3, 4])
335
336  build() {
337    Row() {
338      Column() {
339        Button('Provide init set').onClick(() => {
340          this.message = new Set([0, 1, 2, 3, 4, 5])
341        })
342        Child()
343      }
344      .width('100%')
345    }
346    .height('100%')
347  }
348}
349```
350
351### Support for Union Type
352
353@Provide and @Consume support **undefined**, **null**, and union types. In the following example, the type of **count** is string | undefined. If the attribute or type of **count** is changed when the button in the **Parent** component is clicked, the change will be synced to the child component.
354
355```ts
356@Component
357struct Child {
358  // The @Consume decorated variable is bound to the @Provide decorated variable in its ancestor component Ancestors under the same attribute name.
359  @Consume count: string | undefined;
360
361  build() {
362    Column() {
363      Text(`count(${this.count})`)
364      Button(`count(${this.count}), Child`)
365        .onClick(() => this.count = 'Ancestors')
366    }
367    .width('50%')
368  }
369}
370
371@Component
372struct Parent {
373  build() {
374    Row({ space: 5 }) {
375      Child()
376    }
377  }
378}
379
380@Entry
381@Component
382struct Ancestors {
383  // The @Provide decorated variable count of the union type is provided by the entry component Ancestors for its descendant components.
384  @Provide count: string | undefined = 'Child';
385
386  build() {
387    Column() {
388      Button(`count(${this.count}), Child`)
389        .onClick(() => this.count = undefined)
390      Parent()
391    }
392  }
393}
394```
395
396### Support for the allowOverride Parameter
397
398**allowOverride** allows you to override an existing \@Provide decorated variable.
399
400> **NOTE**
401>
402> This API is supported since API version 11.
403
404| Name  | Type  | Mandatory| Description                                                        |
405| ------ | ------ | ---- | ------------------------------------------------------------ |
406| allowOverride | string | No| Enables overriding for \@Provide. When you define an \@Provide decorated variable, use this parameter to override the existing variable with the same name (if any) in the same component tree. If this parameter is not used, defining a variable whose name is already in use will return an error.|
407
408```ts
409@Component
410struct MyComponent {
411  @Provide({allowOverride : "reviewVotes"}) reviewVotes: number = 10;
412}
413```
414
415```ts
416@Component
417struct GrandSon {
418  // The @Consume decorated variable is bound to the @Provide decorated variable in its ancestor component under the same attribute name.
419  @Consume("reviewVotes") reviewVotes: number;
420
421  build() {
422    Column() {
423      Text(`reviewVotes(${this.reviewVotes})`) // The Text component displays 10.
424      Button(`reviewVotes(${this.reviewVotes}), give +1`)
425        .onClick(() => this.reviewVotes += 1)
426    }
427    .width('50%')
428  }
429}
430
431@Component
432struct Child {
433  @Provide({ allowOverride: "reviewVotes" }) reviewVotes: number = 10;
434
435  build() {
436    Row({ space: 5 }) {
437      GrandSon()
438    }
439  }
440}
441
442@Component
443struct Parent {
444  @Provide({ allowOverride: "reviewVotes" }) reviewVotes: number = 20;
445
446  build() {
447    Child()
448  }
449}
450
451@Entry
452@Component
453struct GrandParent {
454  @Provide("reviewVotes") reviewVotes: number = 40;
455
456  build() {
457    Column() {
458      Button(`reviewVotes(${this.reviewVotes}), give +1`)
459        .onClick(() => this.reviewVotes += 1)
460      Parent()
461    }
462  }
463}
464```
465
466In the preceding example:
467- The **@Provide("reviewVotes") reviewVotes: number = 40** variable is declared in **GrandParent**.
468- In **Parent**, a child component of **GrandParent**, **allowOverride** is declared for **@Provide** to override the **@Provide("reviewVotes") reviewVotes: number = 40** variable of **GrandParent**. If **allowOverride** is not declared, a runtime error is thrown to indicate that the @Provide decorated variable is already in use. The same case applies to **Child**.
469- The @Consume decorated variable of **GrandSon** is initialized from the @Provide decorated variable of its nearest ancestor under the same attribute name.
470- In this example, **GrandSon** finds in the ancestor **Child** the @Provide decorated variable with the same attribute name. Therefore, the initial value of **@Consume("reviewVotes") reviewVotes: number** is **10**. If an @Provide decorated variable with the same attribute name is not defined in **Child**, **GrandSon** continues its search until it finds the one with the same attribute name.
471- If no such a variable is found when **GrandSon** has reached the root node, an error is thrown to indicate that @Provide could not be found for @Consume initialization.
472
473
474## FAQs
475
476### \@Provide Not Defined Error in the Case of a \@BuilderParam Trailing Closure
477
478In the following example, when **CustomWidget** executes **this.builder()** to create the child component **CustomWidgetChild**, **this** points to **HomePage**. As such, the \@Provide decorated variable of **CustomWidget** cannot be found, and an error is thrown. In light of this, exercise caution with **this** when using \@BuilderParam.
479
480Negative example:
481
482```ts
483class Tmp {
484  a: string = ''
485}
486
487@Entry
488@Component
489struct HomePage {
490  @Builder
491  builder2($$: Tmp) {
492    Text(`${$$.a}test`)
493  }
494
495  build() {
496    Column() {
497      CustomWidget() {
498        CustomWidgetChild({ builder: this.builder2 })
499      }
500    }
501  }
502}
503
504@Component
505struct CustomWidget {
506  @Provide('a') a: string = 'abc';
507  @BuilderParam
508  builder: () => void;
509
510  build() {
511    Column() {
512      Button('Hello').onClick(() => {
513        if (this.a == 'ddd') {
514          this.a = 'abc';
515        }
516        else {
517          this.a = 'ddd';
518        }
519
520      })
521      this.builder()
522    }
523  }
524}
525
526@Component
527struct CustomWidgetChild {
528  @Consume('a') a: string;
529  @BuilderParam
530  builder: ($$: Tmp) => void;
531
532  build() {
533    Column() {
534      this.builder({ a: this.a })
535    }
536  }
537}
538```
539
540Positive example:
541
542```ts
543class Tmp {
544  name: string = ''
545}
546
547@Entry
548@Component
549struct HomePage {
550  @Provide('name') name: string = 'abc';
551
552  @Builder
553  builder2($$: Tmp) {
554    Text (`${$$.name}test`)
555  }
556
557  build() {
558    Column() {
559      Button('Hello').onClick(() => {
560        if (this.name == 'ddd') {
561          this.name = 'abc';
562        } else {
563          this.name = 'ddd';
564        }
565      })
566      CustomWidget() {
567        CustomWidgetChild({ builder: this.builder2 })
568      }
569    }
570  }
571}
572
573@Component
574struct CustomWidget {
575  @BuilderParam
576  builder: () => void;
577
578  build() {
579    this.builder()
580  }
581}
582
583@Component
584struct CustomWidgetChild {
585  @Consume('name') name: string;
586  @BuilderParam
587  builder: ($$: Tmp) => void;
588
589  build() {
590    Column() {
591      this.builder({ name: this.name })
592    }
593  }
594}
595```
596