• 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 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![en-us_image_0000001552614217](figures/en-us_image_0000001552614217.png)
77
78
79| \@Consume Transfer/Access| Description                                      |
80| -------------- | ---------------------------------------- |
81| Initialization and update from the parent component    | Forbidden. Initialized from the \@Provide decorated variable with the same name or alias.     |
82| Subnode initialization      | Supported; can be used to initialize an \@State, \@Link, \@Prop, or \@Provide decorated variable in the child component.|
83| Synchronization with the ancestor component       | Two-way with the @Provide decorated variable in the ancestor component.                         |
84| Access     | Private, accessible only within the component.                          |
85
86
87  **Figure 2** \@Consume initialization rule
88
89
90![en-us_image_0000001502094666](figures/en-us_image_0000001502094666.png)
91
92
93## Observed Changes and Behavior
94
95
96### Observed Changes
97
98- When the decorated variable is of the Boolean, string, or number type, its value change can be observed.
99
100- When the decorated variable is of the class or Object type, its value change and value changes of all its attributes, that is, the attributes that **Object.keys(observedObject)** returns, can be observed.
101
102- When the decorated variable is of the array type, the addition, deletion, and updates of array items can be observed.
103
104- When the decorated variable is of the Date type, the overall value assignment of the Date object can be observed, and the following APIs can be called to update Date attributes: **setFullYear**, **setMonth**, **setDate**, **setHours**, **setMinutes**, **setSeconds**, **setMilliseconds**, **setTime**, **setUTCFullYear**, **setUTCMonth**, **setUTCDate**, **setUTCHours**, **setUTCMinutes**, **setUTCSeconds**, and **setUTCMilliseconds**.
105
106```ts
107@Component
108struct 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![Provide_Consume_framework_behavior](figures/Provide_Consume_framework_behavior.png)
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