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 9Before reading this topic, you are advised to read [Basic Syntax Overview](./arkts-basic-syntax-overview.md), [Declarative UI Description](./arkts-declarative-ui-description.md), and [Creating a Custom Component](./arkts-create-custom-components.md) to have an understanding of the basic syntax of the UI paradigm and custom components. 10 11> **NOTE** 12> 13> These two decorators can be used in ArkTS widgets since API version 9. 14> 15> These two decorators can be used in atomic services since API version 11. 16 17## Overview 18 19\@Provide/\@Consume decorated state variables have the following features: 20 21- 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. 22 23- 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. 24 25- \@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. 26 27 28```ts 29// Binding through the same variable name 30@Provide age: number = 0; 31@Consume age: number; 32 33// Binding through the same variable alias 34@Provide('a') id: number = 0; 35@Consume('a') age: number; 36``` 37 38 39When \@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. 40 41 42## Decorator Description 43 44The rules of \@State also apply to \@Provide. The difference is that \@Provide also functions as a synchronization source for multi-layer descendants. 45 46| \@Provide Decorator| Description | 47| -------------- | ---------------------------------------- | 48| 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.| 49| 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.| 50| 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, are supported.<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. | 51| Initial value for the decorated variable | Mandatory. | 52| 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).| 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, are supported. 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 Child { 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 Parent { 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 Child() 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## Constraints 182 1831. The **key** parameter of \@Provider and \@Consumer must be of the string type. Otherwise, an error is reported during compilation. 184 185```ts 186// Incorrect format. An error is reported during compilation. 187let change: number = 10; 188@Provide(change) message: string = 'Hello'; 189 190// Correct format. 191let change: string = 'change'; 192@Provide(change) message: string = 'Hello'; 193``` 194 1952. Variables decorated by \@Consume cannot be initialized locally or using constructor parameters. Otherwise, an error is reported during compilation. \@Consume can be initialized only by matching the corresponding \@Provide variable based on the key. 196 197[Negative example] 198 199```ts 200@Component 201struct Child { 202 @Consume msg: string; 203 // Incorrect format. Local initialization is not allowed. 204 @Consume msg1: string = 'Hello'; 205 206 build() { 207 Text(this.msg) 208 } 209} 210 211@Entry 212@Component 213struct Parent { 214 @Provide message: string = 'Hello'; 215 216 build() { 217 Column() { 218 // Incorrect format. External initialization is not allowed. 219 Child({msg: 'Hello'}) 220 } 221 } 222} 223``` 224 225[Positive example] 226 227```ts 228@Component 229struct Child { 230 @Consume num: number; 231 232 build() { 233 Column() { 234 Text(`Value of num: ${this.num}`) 235 } 236 } 237} 238 239@Entry 240@Component 241struct Parent { 242 @Provide num: number = 10; 243 244 build() { 245 Column() { 246 Text(`Value of num: ${this.num}`) 247 Child() 248 } 249 } 250} 251``` 252 2533. \@When the **key** of \@Provide is defined repeatedly, the framework throws a runtime error to remind you. If you need to define the **key** repeatedly, use [allowoverride](#support-for-the-allowoverride-parameter). 254 255```ts 256// Incorrect format. "a" is defined repeatedly. 257@Provide('a') count: number = 10; 258@Provide('a') num: number = 10; 259 260// Correct format. 261@Provide('a') count: number = 10; 262@Provide('b') num: number = 10; 263``` 264 2654. If you do not define the \@Provide variable of the corresponding key when initializing the \@Consume variable, the framework throws a runtime error, indicating that the \@Consume variable fails to be initialized because the \@Provide variable of the corresponding key cannot be found. 266 267[Negative example] 268 269```ts 270@Component 271struct Child { 272 @Consume num: number; 273 274 build() { 275 Column() { 276 Text(`Value of num: ${this.num}`) 277 } 278 } 279} 280 281@Entry 282@Component 283struct Parent { 284 // Incorrect format. @Provide is missing. 285 num: number = 10; 286 287 build() { 288 Column() { 289 Text(`Value of num: ${this.num}`) 290 Child() 291 } 292 } 293} 294``` 295 296[Positive example] 297 298```ts 299@Component 300struct Child { 301 @Consume num: number; 302 303 build() { 304 Column() { 305 Text(`Value of num: ${this.num}`) 306 } 307 } 308} 309 310@Entry 311@Component 312struct Parent { 313 // Correct format. 314 @Provide num: number = 10; 315 316 build() { 317 Column() { 318 Text(`Value of num: ${this.num}`) 319 Child() 320 } 321 } 322} 323``` 324 3255. \@Provide and \@Consume cannot decorate variables of the function type. Otherwise, the framework throws a runtime error. 326 327 328## Application Scenarios 329 330The following example shows the two-way synchronization between \@Provide and \@Consume decorated variables. When you click the **ToDo** and **ToDoItem** buttons, the **count** changes in both components are synchronized in a two-way manner. 331 332 333 334```ts 335@Component 336struct ToDoItem { 337 // The @Consume decorated variable is bound to the @Provide decorated variable in its ancestor component ToDo under the same attribute name. 338 @Consume count: number; 339 340 build() { 341 Column() { 342 Text(`count(${this.count})`) 343 Button(`count(${this.count}), count + 1`) 344 .onClick(() => this.count += 1) 345 } 346 .width('50%') 347 } 348} 349 350@Component 351struct ToDoList { 352 build() { 353 Row({ space: 5 }) { 354 ToDoItem() 355 ToDoItem() 356 } 357 } 358} 359 360@Component 361struct ToDoDemo { 362 build() { 363 ToDoList() 364 } 365} 366 367@Entry 368@Component 369struct ToDo { 370 // @Provide decorated variable index is provided by the entry component ToDo. 371 @Provide count: number = 0; 372 373 build() { 374 Column() { 375 Button(`count(${this.count}), count + 1`) 376 .onClick(() => this.count += 1) 377 ToDoDemo() 378 } 379 } 380} 381``` 382 383### Decorating Variables of the Map Type 384 385> **NOTE** 386> 387> \@Provide and \@Consume support the Map type since API version 11. 388 389In 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. 390 391```ts 392@Component 393struct Child { 394 @Consume message: Map<number, string> 395 396 build() { 397 Column() { 398 ForEach(Array.from(this.message.entries()), (item: [number, string]) => { 399 Text(`${item[0]}`).fontSize(30) 400 Text(`${item[1]}`).fontSize(30) 401 Divider() 402 }) 403 Button('Consume init map').onClick(() => { 404 this.message = new Map([[0, "a"], [1, "b"], [3, "c"]]) 405 }) 406 Button('Consume set new one').onClick(() => { 407 this.message.set(4, "d") 408 }) 409 Button('Consume clear').onClick(() => { 410 this.message.clear() 411 }) 412 Button('Consume replace the first item').onClick(() => { 413 this.message.set(0, "aa") 414 }) 415 Button('Consume delete the first item').onClick(() => { 416 this.message.delete(0) 417 }) 418 } 419 } 420} 421 422 423@Entry 424@Component 425struct MapSample { 426 @Provide message: Map<number, string> = new Map([[0, "a"], [1, "b"], [3, "c"]]) 427 428 build() { 429 Row() { 430 Column() { 431 Button('Provide init map').onClick(() => { 432 this.message = new Map([[0, "a"], [1, "b"], [3, "c"], [4, "d"]]) 433 }) 434 Child() 435 } 436 .width('100%') 437 } 438 .height('100%') 439 } 440} 441``` 442 443### Decorating Variables of the Set Type 444 445> **NOTE** 446> 447> \@Provide and \@Consume support the Set type since API version 11. 448 449In 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. 450 451```ts 452@Component 453struct Child { 454 @Consume message: Set<number> 455 456 build() { 457 Column() { 458 ForEach(Array.from(this.message.entries()), (item: [number, string]) => { 459 Text(`${item[0]}`).fontSize(30) 460 Divider() 461 }) 462 Button('Consume init set').onClick(() => { 463 this.message = new Set([0, 1, 2, 3, 4]) 464 }) 465 Button('Consume set new one').onClick(() => { 466 this.message.add(5) 467 }) 468 Button('Consume clear').onClick(() => { 469 this.message.clear() 470 }) 471 Button('Consume delete the first one').onClick(() => { 472 this.message.delete(0) 473 }) 474 } 475 .width('100%') 476 } 477} 478 479 480@Entry 481@Component 482struct SetSample { 483 @Provide message: Set<number> = new Set([0, 1, 2, 3, 4]) 484 485 build() { 486 Row() { 487 Column() { 488 Button('Provide init set').onClick(() => { 489 this.message = new Set([0, 1, 2, 3, 4, 5]) 490 }) 491 Child() 492 } 493 .width('100%') 494 } 495 .height('100%') 496 } 497} 498``` 499 500### Support for Union Type 501 502@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. 503 504```ts 505@Component 506struct Child { 507 // The @Consume decorated variable is bound to the @Provide decorated variable in its ancestor component Ancestors under the same attribute name. 508 @Consume count: string | undefined; 509 510 build() { 511 Column() { 512 Text(`count(${this.count})`) 513 Button(`count(${this.count}), Child`) 514 .onClick(() => this.count = 'Ancestors') 515 } 516 .width('50%') 517 } 518} 519 520@Component 521struct Parent { 522 build() { 523 Row({ space: 5 }) { 524 Child() 525 } 526 } 527} 528 529@Entry 530@Component 531struct Ancestors { 532 // The @Provide decorated variable count of the union type is provided by the entry component Ancestors for its descendant components. 533 @Provide count: string | undefined = 'Child'; 534 535 build() { 536 Column() { 537 Button(`count(${this.count}), Child`) 538 .onClick(() => this.count = undefined) 539 Parent() 540 } 541 } 542} 543``` 544 545### Support for the allowOverride Parameter 546 547**allowOverride** allows you to override an existing \@Provide decorated variable. 548 549> **NOTE** 550> 551> This API is supported since API version 11. 552 553| Name | Type | Mandatory| Description | 554| ------ | ------ | ---- | ------------------------------------------------------------ | 555| 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.| 556 557```ts 558@Component 559struct MyComponent { 560 @Provide({allowOverride : "reviewVotes"}) reviewVotes: number = 10; 561} 562``` 563 564The complete sample code is as follows: 565 566```ts 567@Component 568struct GrandSon { 569 // The @Consume decorated variable is bound to the @Provide decorated variable in its ancestor component under the same attribute name. 570 @Consume("reviewVotes") reviewVotes: number; 571 572 build() { 573 Column() { 574 Text(`reviewVotes(${this.reviewVotes})`) // The Text component displays 10. 575 Button(`reviewVotes(${this.reviewVotes}), give +1`) 576 .onClick(() => this.reviewVotes += 1) 577 } 578 .width('50%') 579 } 580} 581 582@Component 583struct Child { 584 @Provide({ allowOverride: "reviewVotes" }) reviewVotes: number = 10; 585 586 build() { 587 Row({ space: 5 }) { 588 GrandSon() 589 } 590 } 591} 592 593@Component 594struct Parent { 595 @Provide({ allowOverride: "reviewVotes" }) reviewVotes: number = 20; 596 597 build() { 598 Child() 599 } 600} 601 602@Entry 603@Component 604struct GrandParent { 605 @Provide("reviewVotes") reviewVotes: number = 40; 606 607 build() { 608 Column() { 609 Button(`reviewVotes(${this.reviewVotes}), give +1`) 610 .onClick(() => this.reviewVotes += 1) 611 Parent() 612 } 613 } 614} 615``` 616 617In the preceding example: 618- The **@Provide("reviewVotes") reviewVotes: number = 40** variable is declared in **GrandParent**. 619- 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**. 620- The @Consume decorated variable of **GrandSon** is initialized from the @Provide decorated variable of its nearest ancestor under the same attribute name. 621- 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. 622- 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. 623 624 625## FAQs 626 627### \@Provide Not Defined Error in the Case of a \@BuilderParam Trailing Closure 628 629In 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. 630 631[Incorrect Example] 632 633```ts 634class Tmp { 635 a: string = '' 636} 637 638@Entry 639@Component 640struct HomePage { 641 @Builder 642 builder2($$: Tmp) { 643 Text(`${$$.a}test`) 644 } 645 646 build() { 647 Column() { 648 CustomWidget() { 649 CustomWidgetChild({ builder: this.builder2 }) 650 } 651 } 652 } 653} 654 655@Component 656struct CustomWidget { 657 @Provide('a') a: string = 'abc'; 658 @BuilderParam 659 builder: () => void; 660 661 build() { 662 Column() { 663 Button('Hello').onClick(() => { 664 if (this.a == 'ddd') { 665 this.a = 'abc'; 666 } 667 else { 668 this.a = 'ddd'; 669 } 670 671 }) 672 this.builder() 673 } 674 } 675} 676 677@Component 678struct CustomWidgetChild { 679 @Consume('a') a: string; 680 @BuilderParam 681 builder: ($$: Tmp) => void; 682 683 build() { 684 Column() { 685 this.builder({ a: this.a }) 686 } 687 } 688} 689``` 690 691[Correct Example] 692 693```ts 694class Tmp { 695 name: string = '' 696} 697 698@Entry 699@Component 700struct HomePage { 701 @Provide('name') name: string = 'abc'; 702 703 @Builder 704 builder2($$: Tmp) { 705 Text (`${$$.name}test`) 706 } 707 708 build() { 709 Column() { 710 Button('Hello').onClick(() => { 711 if (this.name == 'ddd') { 712 this.name = 'abc'; 713 } else { 714 this.name = 'ddd'; 715 } 716 }) 717 CustomWidget() { 718 CustomWidgetChild({ builder: this.builder2 }) 719 } 720 } 721 } 722} 723 724@Component 725struct CustomWidget { 726 @BuilderParam 727 builder: () => void; 728 729 build() { 730 this.builder() 731 } 732} 733 734@Component 735struct CustomWidgetChild { 736 @Consume('name') name: string; 737 @BuilderParam 738 builder: ($$: Tmp) => void; 739 740 build() { 741 Column() { 742 this.builder({ name: this.name }) 743 } 744 } 745} 746``` 747 748### Using the a.b(this.object) Format Fails to Trigger UI Re-render 749 750In the **build** method, when the variable decorated by @Provide and @Consume is of the object type and is called using the **a.b(this.object)** format, the native 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, the UI re-render is not triggered when **this.dog.age** and **this.dog.name** in the component is changed by using a static method or using **this** to call the internal method of the component. 751 752[Negative example] 753 754```ts 755class Animal { 756 name:string; 757 type:string; 758 age: number; 759 760 constructor(name:string, type:string, age:number) { 761 this.name = name; 762 this.type = type; 763 this.age = age; 764 } 765 766 static changeName(animal:Animal) { 767 animal.name = 'Jack'; 768 } 769 static changeAge(animal:Animal) { 770 animal.age += 1; 771 } 772} 773 774@Entry 775@Component 776struct Zoo { 777 @Provide dog:Animal = new Animal('WangCai', 'dog', 2); 778 779 changeZooDogAge(animal:Animal) { 780 animal.age += 2; 781 } 782 783 build() { 784 Column({ space:10 }) { 785 Text(`Zoo: This is a ${this.dog.age}-year-old ${this.dog.type} named ${this.dog.name}.`) 786 .fontColor(Color.Red) 787 .fontSize(30) 788 Button('changeAge') 789 .onClick(()=>{ 790 // The UI cannot be re-rendered using a static method. 791 Animal.changeAge(this.dog); 792 }) 793 Button('changeZooDogAge') 794 .onClick(()=>{ 795 // The UI cannot be re-rendered using this. 796 this.changeZooDogAge(this.dog); 797 }) 798 ZooChild() 799 } 800 } 801} 802 803@Component 804struct ZooChild { 805 806 build() { 807 Column({ space:10 }) { 808 Text(`ZooChild`) 809 .fontColor(Color.Blue) 810 .fontSize(30) 811 ZooGrandChild() 812 } 813 } 814} 815 816@Component 817struct ZooGrandChild { 818 @Consume dog:Animal; 819 820 changeZooGrandChildName(animal:Animal) { 821 animal.name = 'Marry'; 822 } 823 824 build() { 825 Column({ space:10 }) { 826 Text(`ZooGrandChild: This is a ${this.dog.age}-year-old ${this.dog.type} named ${this.dog.name}.`) 827 .fontColor(Color.Yellow) 828 .fontSize(30) 829 Button('changeName') 830 .onClick(()=>{ 831 // The UI cannot be re-rendered using a static method. 832 Animal.changeName(this.dog); 833 }) 834 Button('changeZooGrandChildName') 835 .onClick(()=>{ 836 // The UI cannot be re-rendered using this. 837 this.changeZooGrandChildName(this.dog); 838 }) 839 } 840 } 841} 842``` 843 844You can add a proxy for **this.dog** to re-render the UI by assigning a value to the variable and then calling the variable. 845 846[Positive example] 847 848```ts 849class Animal { 850 name:string; 851 type:string; 852 age: number; 853 854 constructor(name:string, type:string, age:number) { 855 this.name = name; 856 this.type = type; 857 this.age = age; 858 } 859 860 static changeName(animal:Animal) { 861 animal.name = 'Jack'; 862 } 863 static changeAge(animal:Animal) { 864 animal.age += 1; 865 } 866} 867 868@Entry 869@Component 870struct Zoo { 871 @Provide dog:Animal = new Animal('WangCai', 'dog', 2); 872 873 changeZooDogAge(animal:Animal) { 874 animal.age += 2; 875 } 876 877 build() { 878 Column({ space:10 }) { 879 Text(`Zoo: This is a ${this.dog.age}-year-old ${this.dog.type} named ${this.dog.name}.`) 880 .fontColor(Color.Red) 881 .fontSize(30) 882 Button('changeAge') 883 .onClick(()=>{ 884 // Add a proxy by assigning a value. 885 let newDog = this.dog; 886 Animal.changeAge(newDog); 887 }) 888 Button('changeZooDogAge') 889 .onClick(()=>{ 890 // Add a proxy by assigning a value. 891 let newDog = this.dog; 892 this.changeZooDogAge(newDog); 893 }) 894 ZooChild() 895 } 896 } 897} 898 899@Component 900struct ZooChild { 901 902 build() { 903 Column({ space:10 }) { 904 Text(`ZooChild.`) 905 .fontColor(Color.Blue) 906 .fontSize(30) 907 ZooGrandChild() 908 } 909 } 910} 911 912@Component 913struct ZooGrandChild { 914 @Consume dog:Animal; 915 916 changeZooGrandChildName(animal:Animal) { 917 animal.name = 'Marry'; 918 } 919 920 build() { 921 Column({ space:10 }) { 922 Text(`ZooGrandChild: This is a ${this.dog.age}-year-old ${this.dog.type} named ${this.dog.name}.`) 923 .fontColor(Color.Yellow) 924 .fontSize(30) 925 Button('changeName') 926 .onClick(()=>{ 927 // Add a proxy by assigning a value. 928 let newDog = this.dog; 929 Animal.changeName(newDog); 930 }) 931 Button('changeZooGrandChildName') 932 .onClick(()=>{ 933 // Add a proxy by assigning a value. 934 let newDog = this.dog; 935 this.changeZooGrandChildName(newDog); 936 }) 937 } 938 } 939} 940``` 941