• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 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 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
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![en-us_image_0000001552614217](figures/en-us_image_0000001552614217.png)
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![en-us_image_0000001502094666](figures/en-us_image_0000001502094666.png)
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![Provide_Consume_framework_behavior](figures/Provide_Consume_framework_behavior.png)
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