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 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 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 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