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 "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 UI paradigms. 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 "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 62## Variable Transfer/Access Rules 63 64 65| \@Provide Transfer/Access| Description | 66| -------------- | ---------------------------------------- | 67| 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.| 68| Subnode initialization | Supported; can be used to initialize an \@State, \@Link, \@Prop, or \@Provide decorated variable in the child component.| 69| Synchronization with the parent component | Not supported. | 70| Synchronization with descendant components | Two-way with @Consume decorated variables in descendant components. | 71| Access | Private, accessible only within the component. | 72 73 74 **Figure 1** \@Provide initialization rule 75 76 77 78 79 80| \@Consume Transfer/Access| Description | 81| -------------- | ---------------------------------------- | 82| Initialization and update from the parent component | Forbidden. Initialized from the \@Provide decorated variable with the same name or alias. | 83| Subnode initialization | Supported; can be used to initialize an \@State, \@Link, \@Prop, or \@Provide decorated variable in the child component.| 84| Synchronization with the ancestor component | Two-way with the @Provide decorated variable in the ancestor component. | 85| Access | Private, accessible only within the component. | 86 87 88 **Figure 2** \@Consume initialization rule 89 90 91 92 93 94## Observed Changes and Behavior 95 96 97### Observed Changes 98 99- When the decorated variable is of the Boolean, string, or number type, its value change can be observed. 100 101- 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. 102 103- When the decorated variable is of the array type, the addition, deletion, and updates of array items can be observed. 104 105- 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**. 106 107```ts 108@Component 109struct Child { 110 @Consume selectedDate: Date; 111 112 build() { 113 Column() { 114 Button(`child increase the day by 1`) 115 .onClick(() => { 116 this.selectedDate.setDate(this.selectedDate.getDate() + 1) 117 }) 118 Button('child update the new date') 119 .margin(10) 120 .onClick(() => { 121 this.selectedDate = new Date('2023-09-09') 122 }) 123 DatePicker({ 124 start: new Date('1970-1-1'), 125 end: new Date('2100-1-1'), 126 selected: this.selectedDate 127 }) 128 } 129 } 130} 131 132@Entry 133@Component 134struct Parent { 135 @Provide selectedDate: Date = new Date('2021-08-08') 136 137 build() { 138 Column() { 139 Button('parent increase the day by 1') 140 .margin(10) 141 .onClick(() => { 142 this.selectedDate.setDate(this.selectedDate.getDate() + 1) 143 }) 144 Button('parent update the new date') 145 .margin(10) 146 .onClick(() => { 147 this.selectedDate = new Date('2023-07-07') 148 }) 149 DatePicker({ 150 start: new Date('1970-1-1'), 151 end: new Date('2100-1-1'), 152 selected: this.selectedDate 153 }) 154 Child() 155 } 156 } 157} 158``` 159 160- 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). 161 162- 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). 163 164### Framework Behavior 165 1661. Initial render: 167 1. The \@Provide decorated variable is passed to all child components of the owning component in map mode. 168 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. 169 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. 170 1712. When the \@Provide decorated variable is updated: 172 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. After the \@Provide decorated variable of the parent component is changed, all system components (**elementId**) and state variables (\@Consume) that depend on the parent component are traversed and updated. 173 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. 174 1753. When the \@Consume decorated variable is updated: 176 177 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. 178 179 180 181 182## Constraints 183 1841. The **key** parameter of \@Provide and \@Consume must be of the string type. Otherwise, an error is reported during compilation. 185 186 ```ts 187 // Incorrect format. An error is reported during compilation. 188 let change: number = 10; 189 @Provide(change) message: string = 'Hello'; 190 191 // Correct format. 192 let change: string = 'change'; 193 @Provide(change) message: string = 'Hello'; 194 ``` 195 1962. 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. 197 198 [Negative example] 199 200 ```ts 201 @Component 202 struct Child { 203 @Consume msg: string; 204 // Incorrect format. Local initialization is not allowed. 205 @Consume msg1: string = 'Hello'; 206 207 build() { 208 Text(this.msg) 209 } 210 } 211 212 @Entry 213 @Component 214 struct Parent { 215 @Provide message: string = 'Hello'; 216 217 build() { 218 Column() { 219 // Incorrect format. External initialization is not allowed. 220 Child({msg: 'Hello'}) 221 } 222 } 223 } 224 ``` 225 226 [Positive example] 227 228 ```ts 229 @Component 230 struct Child { 231 @Consume num: number; 232 233 build() { 234 Column() { 235 Text(`Value of num: ${this.num}`) 236 } 237 } 238 } 239 240 @Entry 241 @Component 242 struct Parent { 243 @Provide num: number = 10; 244 245 build() { 246 Column() { 247 Text(`Value of num: ${this.num}`) 248 Child() 249 } 250 } 251 } 252 ``` 253 2543. \@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). 255 256 ```ts 257 // Incorrect format. "a" is defined repeatedly. 258 @Provide('a') count: number = 10; 259 @Provide('a') num: number = 10; 260 261 // Correct format. 262 @Provide('a') count: number = 10; 263 @Provide('b') num: number = 10; 264 ``` 265 2664. 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. 267 268 [Negative example] 269 270 ```ts 271 @Component 272 struct Child { 273 @Consume num: number; 274 275 build() { 276 Column() { 277 Text(`Value of num: ${this.num}`) 278 } 279 } 280 } 281 282 @Entry 283 @Component 284 struct Parent { 285 // Incorrect format. @Provide is missing. 286 num: number = 10; 287 288 build() { 289 Column() { 290 Text(`Value of num: ${this.num}`) 291 Child() 292 } 293 } 294 } 295 ``` 296 297 [Positive example] 298 299 ```ts 300 @Component 301 struct Child { 302 @Consume num: number; 303 304 build() { 305 Column() { 306 Text(`Value of num: ${this.num}`) 307 } 308 } 309 } 310 311 @Entry 312 @Component 313 struct Parent { 314 // Correct format. 315 @Provide num: number = 10; 316 317 build() { 318 Column() { 319 Text(`Value of num: ${this.num}`) 320 Child() 321 } 322 } 323 } 324 ``` 325 3265. \@Provide and \@Consume cannot decorate variables of the function type. Otherwise, the framework throws a runtime error. 327 328 329## Use Scenarios 330 331The 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. 332 333```ts 334@Component 335struct ToDoItem { 336 // The @Consume decorated variable is bound to the @Provide decorated variable in its ancestor component ToDo under the same attribute name. 337 @Consume count: number; 338 339 build() { 340 Column() { 341 Text(`count(${this.count})`) 342 Button(`count(${this.count}), count + 1`) 343 .onClick(() => this.count += 1) 344 } 345 .width('50%') 346 } 347} 348 349@Component 350struct ToDoList { 351 build() { 352 Row({ space: 5 }) { 353 ToDoItem() 354 ToDoItem() 355 } 356 } 357} 358 359@Component 360struct ToDoDemo { 361 build() { 362 ToDoList() 363 } 364} 365 366@Entry 367@Component 368struct ToDo { 369 // @Provide decorated variable index is provided by the entry component ToDo. 370 @Provide count: number = 0; 371 372 build() { 373 Column() { 374 Button(`count(${this.count}), count + 1`) 375 .onClick(() => this.count += 1) 376 ToDoDemo() 377 } 378 } 379} 380``` 381 382### Decorating Variables of the Map Type 383 384> **NOTE** 385> 386> \@Provide and \@Consume support the Map type since API version 11. 387 388In 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. 389 390```ts 391@Component 392struct Child { 393 @Consume message: Map<number, string> 394 395 build() { 396 Column() { 397 ForEach(Array.from(this.message.entries()), (item: [number, string]) => { 398 Text(`${item[0]}`).fontSize(30) 399 Text(`${item[1]}`).fontSize(30) 400 Divider() 401 }) 402 Button('Consume init map').onClick(() => { 403 this.message = new Map([[0, "a"], [1, "b"], [3, "c"]]) 404 }) 405 Button('Consume set new one').onClick(() => { 406 this.message.set(4, "d") 407 }) 408 Button('Consume clear').onClick(() => { 409 this.message.clear() 410 }) 411 Button('Consume replace the first item').onClick(() => { 412 this.message.set(0, "aa") 413 }) 414 Button('Consume delete the first item').onClick(() => { 415 this.message.delete(0) 416 }) 417 } 418 } 419} 420 421 422@Entry 423@Component 424struct MapSample { 425 @Provide message: Map<number, string> = new Map([[0, "a"], [1, "b"], [3, "c"]]) 426 427 build() { 428 Row() { 429 Column() { 430 Button('Provide init map').onClick(() => { 431 this.message = new Map([[0, "a"], [1, "b"], [3, "c"], [4, "d"]]) 432 }) 433 Child() 434 } 435 .width('100%') 436 } 437 .height('100%') 438 } 439} 440``` 441 442### Decorating Variables of the Set Type 443 444> **NOTE** 445> 446> \@Provide and \@Consume support the Set type since API version 11. 447 448In 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. 449 450```ts 451@Component 452struct Child { 453 @Consume message: Set<number> 454 455 build() { 456 Column() { 457 ForEach(Array.from(this.message.entries()), (item: [number, string]) => { 458 Text(`${item[0]}`).fontSize(30) 459 Divider() 460 }) 461 Button('Consume init set').onClick(() => { 462 this.message = new Set([0, 1, 2, 3, 4]) 463 }) 464 Button('Consume set new one').onClick(() => { 465 this.message.add(5) 466 }) 467 Button('Consume clear').onClick(() => { 468 this.message.clear() 469 }) 470 Button('Consume delete the first one').onClick(() => { 471 this.message.delete(0) 472 }) 473 } 474 .width('100%') 475 } 476} 477 478 479@Entry 480@Component 481struct SetSample { 482 @Provide message: Set<number> = new Set([0, 1, 2, 3, 4]) 483 484 build() { 485 Row() { 486 Column() { 487 Button('Provide init set').onClick(() => { 488 this.message = new Set([0, 1, 2, 3, 4, 5]) 489 }) 490 Child() 491 } 492 .width('100%') 493 } 494 .height('100%') 495 } 496} 497``` 498 499### Support for Union Type 500 501@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. 502 503```ts 504@Component 505struct Child { 506 // The @Consume decorated variable is bound to the @Provide decorated variable in its ancestor component Ancestors under the same attribute name. 507 @Consume count: string | undefined; 508 509 build() { 510 Column() { 511 Text(`count(${this.count})`) 512 Button(`count(${this.count}), Child`) 513 .onClick(() => this.count = 'Ancestors') 514 } 515 .width('50%') 516 } 517} 518 519@Component 520struct Parent { 521 build() { 522 Row({ space: 5 }) { 523 Child() 524 } 525 } 526} 527 528@Entry 529@Component 530struct Ancestors { 531 // The @Provide decorated variable count of the union type is provided by the entry component Ancestors for its descendant components. 532 @Provide count: string | undefined = 'Child'; 533 534 build() { 535 Column() { 536 Button(`count(${this.count}), Child`) 537 .onClick(() => this.count = undefined) 538 Parent() 539 } 540 } 541} 542``` 543 544### Support for the allowOverride Parameter 545 546**allowOverride** allows you to override an existing \@Provide decorated variable. 547 548> **NOTE** 549> 550> This API is supported since API version 11. 551 552| Name | Type | Mandatory| Description | 553| ------ | ------ | ---- | ------------------------------------------------------------ | 554| 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.| 555 556```ts 557@Component 558struct MyComponent { 559 @Provide({allowOverride : "reviewVotes"}) reviewVotes: number = 10; 560} 561``` 562 563The complete sample code is as follows: 564 565```ts 566@Component 567struct GrandSon { 568 // The @Consume decorated variable is bound to the @Provide decorated variable in its ancestor component under the same attribute name. 569 @Consume("reviewVotes") reviewVotes: number; 570 571 build() { 572 Column() { 573 Text(`reviewVotes(${this.reviewVotes})`) // The Text component displays 10. 574 Button(`reviewVotes(${this.reviewVotes}), give +1`) 575 .onClick(() => this.reviewVotes += 1) 576 } 577 .width('50%') 578 } 579} 580 581@Component 582struct Child { 583 @Provide({ allowOverride: "reviewVotes" }) reviewVotes: number = 10; 584 585 build() { 586 Row({ space: 5 }) { 587 GrandSon() 588 } 589 } 590} 591 592@Component 593struct Parent { 594 @Provide({ allowOverride: "reviewVotes" }) reviewVotes: number = 20; 595 596 build() { 597 Child() 598 } 599} 600 601@Entry 602@Component 603struct GrandParent { 604 @Provide("reviewVotes") reviewVotes: number = 40; 605 606 build() { 607 Column() { 608 Button(`reviewVotes(${this.reviewVotes}), give +1`) 609 .onClick(() => this.reviewVotes += 1) 610 Parent() 611 } 612 } 613} 614``` 615 616In the preceding example: 617- The **@Provide("reviewVotes") reviewVotes: number = 40** variable is declared in **GrandParent**. 618- 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**. 619- The @Consume decorated variable of **GrandSon** is initialized from the @Provide decorated variable of its nearest ancestor under the same attribute name. 620- 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 as @Consume decorated variable is not defined in **Child**, **GrandSon** continues its search in **Parent** until it finds the one decorated by @Provide with the same attribute name, whose value is **20**. 621- 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. 622 623 624## FAQs 625 626### \@Provide Not Defined Error in the Case of a \@BuilderParam Trailing Closure 627 628In 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. 629 630[Incorrect Example] 631 632```ts 633class Tmp { 634 a: string = '' 635} 636 637@Entry 638@Component 639struct HomePage { 640 @Builder 641 builder2($$: Tmp) { 642 Text(`${$$.a}test`) 643 } 644 645 build() { 646 Column() { 647 CustomWidget() { 648 CustomWidgetChild({ builder: this.builder2 }) 649 } 650 } 651 } 652} 653 654@Component 655struct CustomWidget { 656 @Provide('a') a: string = 'abc'; 657 @BuilderParam 658 builder: () => void; 659 660 build() { 661 Column() { 662 Button('Hello').onClick(() => { 663 if (this.a == 'ddd') { 664 this.a = 'abc'; 665 } 666 else { 667 this.a = 'ddd'; 668 } 669 670 }) 671 this.builder() 672 } 673 } 674} 675 676@Component 677struct CustomWidgetChild { 678 @Consume('a') a: string; 679 @BuilderParam 680 builder: ($$: Tmp) => void; 681 682 build() { 683 Column() { 684 this.builder({ a: this.a }) 685 } 686 } 687} 688``` 689 690[Correct Example] 691 692```ts 693class Tmp { 694 name: string = '' 695} 696 697@Entry 698@Component 699struct HomePage { 700 @Provide('name') name: string = 'abc'; 701 702 @Builder 703 builder2($$: Tmp) { 704 Text (`${$$.name}test`) 705 } 706 707 build() { 708 Column() { 709 Button('Hello').onClick(() => { 710 if (this.name == 'ddd') { 711 this.name = 'abc'; 712 } else { 713 this.name = 'ddd'; 714 } 715 }) 716 CustomWidget() { 717 CustomWidgetChild({ builder: this.builder2 }) 718 } 719 } 720 } 721} 722 723@Component 724struct CustomWidget { 725 @BuilderParam 726 builder: () => void; 727 728 build() { 729 this.builder() 730 } 731} 732 733@Component 734struct CustomWidgetChild { 735 @Consume('name') name: string; 736 @BuilderParam 737 builder: ($$: Tmp) => void; 738 739 build() { 740 Column() { 741 this.builder({ name: this.name }) 742 } 743 } 744} 745``` 746 747### Using the a.b(this.object) Format Fails to Trigger UI Re-render 748 749In 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 original object of **this.object** is passed in the b method. If the property of **this.object** is changed, the UI cannot be re-rendered. In the following example, 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. 750 751[Negative example] 752 753```ts 754class Animal { 755 name:string; 756 type:string; 757 age: number; 758 759 constructor(name:string, type:string, age:number) { 760 this.name = name; 761 this.type = type; 762 this.age = age; 763 } 764 765 static changeName(animal:Animal) { 766 animal.name = 'Jack'; 767 } 768 static changeAge(animal:Animal) { 769 animal.age += 1; 770 } 771} 772 773@Entry 774@Component 775struct Zoo { 776 @Provide dog:Animal = new Animal('WangCai', 'dog', 2); 777 778 changeZooDogAge(animal:Animal) { 779 animal.age += 2; 780 } 781 782 build() { 783 Column({ space:10 }) { 784 Text(`Zoo: This is a ${this.dog.age}-year-old ${this.dog.type} named ${this.dog.name}.`) 785 .fontColor(Color.Red) 786 .fontSize(30) 787 Button('changeAge') 788 .onClick(()=>{ 789 // The UI cannot be re-rendered using a static method. 790 Animal.changeAge(this.dog); 791 }) 792 Button('changeZooDogAge') 793 .onClick(()=>{ 794 // The UI cannot be re-rendered using this. 795 this.changeZooDogAge(this.dog); 796 }) 797 ZooChild() 798 } 799 } 800} 801 802@Component 803struct ZooChild { 804 805 build() { 806 Column({ space:10 }) { 807 Text(`ZooChild`) 808 .fontColor(Color.Blue) 809 .fontSize(30) 810 ZooGrandChild() 811 } 812 } 813} 814 815@Component 816struct ZooGrandChild { 817 @Consume dog:Animal; 818 819 changeZooGrandChildName(animal:Animal) { 820 animal.name = 'Marry'; 821 } 822 823 build() { 824 Column({ space:10 }) { 825 Text(`ZooGrandChild: This is a ${this.dog.age}-year-old ${this.dog.type} named ${this.dog.name}.`) 826 .fontColor(Color.Yellow) 827 .fontSize(30) 828 Button('changeName') 829 .onClick(()=>{ 830 // The UI cannot be re-rendered using a static method. 831 Animal.changeName(this.dog); 832 }) 833 Button('changeZooGrandChildName') 834 .onClick(()=>{ 835 // The UI cannot be re-rendered using this. 836 this.changeZooGrandChildName(this.dog); 837 }) 838 } 839 } 840} 841``` 842 843You can add a proxy for **this.dog** to re-render the UI by assigning a value to the variable and then calling the variable. 844 845[Positive example] 846 847```ts 848class Animal { 849 name:string; 850 type:string; 851 age: number; 852 853 constructor(name:string, type:string, age:number) { 854 this.name = name; 855 this.type = type; 856 this.age = age; 857 } 858 859 static changeName(animal:Animal) { 860 animal.name = 'Jack'; 861 } 862 static changeAge(animal:Animal) { 863 animal.age += 1; 864 } 865} 866 867@Entry 868@Component 869struct Zoo { 870 @Provide dog:Animal = new Animal('WangCai', 'dog', 2); 871 872 changeZooDogAge(animal:Animal) { 873 animal.age += 2; 874 } 875 876 build() { 877 Column({ space:10 }) { 878 Text(`Zoo: This is a ${this.dog.age}-year-old ${this.dog.type} named ${this.dog.name}.`) 879 .fontColor(Color.Red) 880 .fontSize(30) 881 Button('changeAge') 882 .onClick(()=>{ 883 // Add a proxy by assigning a value. 884 let newDog = this.dog; 885 Animal.changeAge(newDog); 886 }) 887 Button('changeZooDogAge') 888 .onClick(()=>{ 889 // Add a proxy by assigning a value. 890 let newDog = this.dog; 891 this.changeZooDogAge(newDog); 892 }) 893 ZooChild() 894 } 895 } 896} 897 898@Component 899struct ZooChild { 900 901 build() { 902 Column({ space:10 }) { 903 Text(`ZooChild.`) 904 .fontColor(Color.Blue) 905 .fontSize(30) 906 ZooGrandChild() 907 } 908 } 909} 910 911@Component 912struct ZooGrandChild { 913 @Consume dog:Animal; 914 915 changeZooGrandChildName(animal:Animal) { 916 animal.name = 'Marry'; 917 } 918 919 build() { 920 Column({ space:10 }) { 921 Text(`ZooGrandChild: This is a ${this.dog.age}-year-old ${this.dog.type} named ${this.dog.name}.`) 922 .fontColor(Color.Yellow) 923 .fontSize(30) 924 Button('changeName') 925 .onClick(()=>{ 926 // Add a proxy by assigning a value. 927 let newDog = this.dog; 928 Animal.changeName(newDog); 929 }) 930 Button('changeZooGrandChildName') 931 .onClick(()=>{ 932 // Add a proxy by assigning a value. 933 let newDog = this.dog; 934 this.changeZooGrandChildName(newDog); 935 }) 936 } 937 } 938} 939``` 940