1# \@Provider and \@Consumer Decorators: Synchronizing Across Component Levels in a Two-Way Manner
2
3\@Provider and \@Consumer are used for synchronizing data across the component levels in a two-way manner, so that you are free from the constraints of the component levels.
4\@Provider and \@Consumer are decorators in state management V2, so they can be used only in \@ComponentV2. A compilation error will be reported if they are used in \@Component.
5
6>**NOTE**
7>
8>\@Provider and \@Consumer decorators are supported since API version 12.
9>
10>State management V2 is still under development, and some features may be incomplete or not always work as expected.
11
12## Overview
13
14\@Provider provides data. Its child components can use the \@Consumer to obtain the data by binding the same **key**.
15\@Consumer obtains data. It can obtain the \@Provider data of the nearest parent node by binding the same **key**. If the \@Provider data cannot be found, the local default value will be used.
16Data types decorated by \@Provider and \@Consumer must be the same.
17
18
19The following notes must be paid attention to when using \@Provider and \@Consumer:
20- \@Provider and \@Consumer both depend on the custom component levels. \@Consumer decorated component will be initialized to different values due to different parent components.
21- Using \@Provider with \@Consumer is equivalent to bonding components together. From the perspective of independent component, usage of \@Provider and \@Consumer should be lessened.
22
23
24## Capability Comparison: \@Provider and \@Consumer Vs. \@Provide and \@Consume
25In state management V1, [\@Provide and \@Consume](./arkts-provide-and-consume.md) are the decorators which provide two-way synchronization across component levels. This topic introduces \@Provider and \@Consumer decorators in state management V2. Although the names and features of the two pairs are similar, there are still some differences.
26If you are not familiar with \@Provide and \@Consume in state management V1, please skip this section.
27
28| Capability| Decorators in V2: \@Provider and \@Consumer                                            |Decorators in V1: \@Provide and \@Consume|
29| ------------------ | ----------------------------------------------------- |----------------------------------------------------- |
30| \@Consume(r)         |Local initialization is allowed. Local default value will be used when \@Provider is not found.| Local initialization is forbidden. An exception will be thrown when the \@Provide is not found.|
31| Supported type          | **function** is supported.| **function** is not supported.|
32| Observation capability          | Only the value change of itself can be observed. To observe the nesting scenario, use this decorator together with [\@Trace](https://gitee.com/openharmony/docs/blob/master/en/application-dev/quick-start/arkts-new-observedV2-and-trace.md).| Changes at the first layer can be observed. To observe the nesting scenario, use this decorator together with [\@Observed and \@ObjectLink](https://gitee.com/openharmony/docs/blob/master/en/application-dev/quick-start/arkts-observed-and-objectlink.md).|
33| alias and attribute name        | **alias** is the unique matching **key** and the default attribute name.| If both the **alias** and attribute name are **key**, the former one is matched first. If no match is found, the attribute name can be matched.|
34| \@Provide(r) initialization from the parent component     | Forbidden.| Allowed.|
35| \@Provide(r) overloading support | Enabled by default. That is, \@Provider can have duplicate names and \@Consumer can search upwards for the nearest \@Provider.| Disabled by default. That is, \@Provide with duplicate names is not allowed in the component tree. If overloading is required, set **allowOverride**.|
36
37
38## Decorator Description
39
40### Basic Rules
41\@Provider syntax:
42`@Provider(alias?: string) varName : varType = initValue`
43
44| \@Provider Property Decorator| Description                                                 |
45| ------------------ | ----------------------------------------------------- |
46| Decorator parameters        | **aliasName?: string**: alias. The default value is the attribute name.|
47| Supported type          | Member variables in the custom component. Property types include number, string, boolean, class, Array, Date, Map, and Set. The [arrow function](#decorating-callback-by-using-provider-and-consumer-and-facilitating-behavior-abstraction-between-components) can be decorated.|
48| Initialization from the parent component     | Forbidden.|
49| Local initialization        | Required.|
50| Observation capability        | Be equivalent to \@Trace. Changes will be synchronized to the corresponding \@Consumer.|
51
52\@Consumer syntax:
53`@Consumer(alias?: string) varName : varType = initValue`
54
55
56| \@Consumer Property Decorator| Description                                                        |
57| --------------------- | ------------------------------------------------------------ |
58| Decorator parameters           | **aliasName?: string**: alias. The default value is the attribute name. The nearest \@Provider is searched upwards.   |
59| Allowed variable types         | Member variables in the custom component. Property types include number, string, boolean, class, Array, Date, Map, and Set. The arrow function can be decorated.|
60| Initialization from the parent component     | Forbidden.|
61| Local initialization        | Required.|
62| Observation capability        | Be equivalent to \@Trace. Changes will be synchronized to the corresponding \@Provider.|
63
64### aliasName and Attribute Name
65\@Provider and \@Consumer accept the optional parameter **aliasName**. If the parameter is not set, the attribute name will be used as the default value. Note: **aliasName** is the unique key for \@Provider and \@Consumer matching.
66
67The following three examples describe how \@Provider and \@Consumer use **aliasName** to search for relationships.
68```ts
69@ComponentV2 struct Parent {
70    @Provider() str: string = 'hello';   // no aliasName, use propertyName "str" as aliasName
71}
72
73@ComponentV2 struct Child {
74    @Consumer('str') str: string = 'world';   // use aliasName 'str' to find
75                                              // can find in Parent, use Provider value 'hello'
76}
77```
78
79```ts
80@ComponentV2 struct Parent {
81    @Provider('alias') str: string = 'hello';   // has alias
82}
83
84@ComponentV2 struct Child {
85    @Consumer('alias') str: string = 'world';   // use aliasName 'alias' to find Provider value 'hello'
86}
87```
88
89```ts
90@ComponentV2 struct Parent {
91    @Provider('alias') str: string = 'hello';   // has alias
92}
93
94@ComponentV2 struct Child {
95    @Consumer() str: string = 'world';   // no aliasName, use propertyName "str" as aliasName, cannot find Provider, so use the local value 'world'
96}
97```
98
99## Constraints
1001. \@Provider and \@Consumer are property decorators for custom components. They can only modify the attributes of custom components and cannot modify the class attributes.
1012. \@Provider and \@Consumer are new state management decorators, which can be used only in \@ComponentV2 but not in \@Component.
102
103## Use Scenarios
104
105### Synchronizing \@Provider and \@Consumer in a Two-Way Manner
106#### Establish a Two-Way Binding
1071. Initialize the **Parent** and **Child** custom components:
108    - **@Consumer() str: string = 'world'** in the **Child** component searches upwards to find **@Provider() str: string = 'hello'** in the **Parent** component.
109    - **@Consumer() str: string = 'world'** is initialized to the value of **@Provider**, that is, **'hello'**.
110    - Both of them establish a two-way synchronization relationship.
1112. Click the button in the **Parent** component to change the @Provider decorated **str** and notify the corresponding @Consumer. UI will be updated.
1123. Click the button in the **Child** component to change the @Consumer decorated **str** and notify the corresponding @Provider. UI will be updated.
113
114```ts
115@Entry
116@ComponentV2
117struct Parent {
118  @Provider() str: string = 'hello';
119
120  build() {
121    Column() {
122      Button(this.str)
123        .onClick(() => {
124          this.str += '0';
125        })
126      Child()
127    }
128  }
129}
130
131
132@ComponentV2
133struct Child {
134  @Consumer() str: string = 'world';
135
136  build() {
137    Column() {
138      Button(this.str)
139        .onClick(() => {
140          this.str += '0';
141        })
142    }
143  }
144}
145```
146#### Fail to Establish a Two-Way Binding
147
148In the following example, \@Provider and \@Consumer fail to establish a two-way synchronization relationship because of different **key** value.
1491. Initialize the **Parent** and **Child** custom components:
150    - @Provider is not found when **@Consumer() str: string = 'world'** in the **Child** component searches upwards.
151    - **@Consumer() str: string = 'world'** uses the local default value 'world'.
152    - Both of them fail to establish a two-way synchronization relationship.
1532. Click the button in the **Parent** component to change @Provider decorated **str1** and refresh the **Button** component associated with @Provider.
1543. Click the button in the **Child** component to change the @Consumer decorated **str** and refresh the **Button** component associated with @Consumer.
155
156```ts
157@Entry
158@ComponentV2
159struct Parent {
160  @Provider() str1: string = 'hello';
161
162  build() {
163    Column() {
164      Button(this.str1)
165        .onClick(() => {
166          this.str1 += '0';
167        })
168      Child()
169    }
170  }
171}
172
173
174@ComponentV2
175struct Child {
176  @Consumer() str: string = 'world';
177
178  build() {
179    Column() {
180      Button(this.str)
181        .onClick(() => {
182          this.str += '0';
183        })
184    }
185  }
186}
187```
188
189### Decorating Callback by Using \@Provider and \@Consumer and Facilitating Behavior Abstraction Between Components
190
191To register a callback function for a child component in a parent component, you can use \@Provider and \@Consumer to decorate a callback.
192For instance, when a drag event occurs, if you want to synchronize the start position of a child component to the parent component, see the following example:
193
194```ts
195@Entry
196@ComponentV2
197struct Parent {
198  @Local childX: number = 0;
199  @Local childY: number = 1;
200  @Provider() onDrag: (x: number, y: number) => void = (x: number, y: number) => {
201    console.log(`onDrag event at x=${x} y:${y}`);
202    this.childX = x;
203    this.childY = y;
204  }
205
206  build() {
207    Column() {
208      Text(`child position x: ${this.childX}, y: ${this.childY}`)
209      Child()
210    }
211  }
212}
213
214@ComponentV2
215struct Child {
216  @Consumer() onDrag: (x: number, y: number) => void = (x: number, y: number) => {};
217
218  build() {
219    Button("changed")
220      .draggable(true)
221      .onDragStart((event: DragEvent) => {
222        // Current previewer does not support common drag events.
223        this.onDrag(event.getDisplayX(), event.getDisplayY());
224      })
225  }
226}
227```
228
229
230### Decorating Complex Types by Using \@Provider, \@Consumer, and \@Trace
231
2321. \@Provider and \@Consumer can only observe the changes of the data. If they are used to decorate complex data types and you need to observe the changes of the attributes, \@Trace is also required.
2332. Changes of some APIs can be observed when modifying **buildin** types, such as Array, Map, Set, and Data. The observation capability is the same as that of [\@Trace](./arkts-new-observedV2-and-trace.md#observed-changes).
234
235```ts
236@ObservedV2
237class User {
238  @Trace name: string;
239  @Trace age: number;
240
241  constructor(name: string, age: number) {
242    this.name = name;
243    this.age = age;
244  }
245}
246
247const data: User[] = [new User('Json', 10), new User('Eric', 15)];
248
249@Entry
250@ComponentV2
251struct Parent {
252  @Provider('data') users: User[] = data;
253
254  build() {
255    Column() {
256      Child()
257      Button('age new user')
258        .onClick(() => {
259          this.users.push(new User('Molly', 18));
260        })
261      Button('age++')
262        .onClick(() => {
263          this.users[0].age++;
264        })
265      Button('change name')
266        .onClick(() => {
267          this.users[0].name = 'Shelly';
268        })
269    }
270  }
271}
272
273
274@ComponentV2
275struct Child {
276  @Consumer('data') users: User[] = [];
277
278  build() {
279    Column() {
280      ForEach(this.users, (item: User) => {
281        Column() {
282          Text(`name: ${item.name}`).fontSize(30)
283          Text(`age: ${item.age}`).fontSize(30)
284          Divider()
285        }
286      })
287    }
288  }
289}
290```
291
292### Searching Upwards by \@Consumer for the Nearest \@Provider
293If \@Provider has duplicate names in the component tree, \@Consumer will search upwards for the \@Provider data of the nearest parent node.
294- In **AComp**, \@Consumer searches upwards to find **@Provider() val: number = 10** defined in the **Parent** component. Therefore, the value is initialized to 10.
295- In **A1Comp**, \@Consumer searches upwards to find **@Provider() val: number = 20** defined in **AComp** and stops searching when it is found. Therefore, the value is initialized to 20.
296
297```ts
298@Entry
299@ComponentV2
300struct Parent {
301  @Provider() val: number = 10;
302
303  build() {
304    Column() {
305      AComp()
306    }
307
308  }
309}
310
311@ComponentV2
312struct AComp {
313  @Provider() val: number = 20;
314  @Consumer("val") val2: number = 0; // 10
315
316  build() {
317    Column() {
318      Text(`${this.val2}`)
319      A1Comp()
320    }
321
322  }
323}
324
325@ComponentV2
326struct A1Comp {
327  @Consumer() val: number = 0; // 20
328
329  build() {
330    Text(`${this.val}`)
331  }
332}
333```
334
335### Initializing \@Param by \@Provider and \@Consumer
336- Click **Text** (**@Consumer val: ${this.val}**) to trigger the change of **@Consumer() val**. This change will be synchronized to **@Provider() val** in the **Parent** component, triggering the change of the **Text** (**@Param val2: ${this.val2}**) in the **Child** component.
337- The change of **@Consumer() val** is also synchronized to **A1Comp**, triggering the change of **Text** (**A1Comp @Param val ${this.val}**).
338
339```ts
340@Entry
341@ComponentV2
342struct Parent {
343  @Provider() val: number = 10;
344
345  build() {
346    Column() {
347      AComp({ val2: this.val })
348    }
349  }
350}
351
352@ComponentV2
353struct AComp {
354  @Consumer() val: number = 0;
355  @Param val2: number = 0;
356
357  build() {
358    Column() {
359      Text(`AComp @Consumer val: ${this.val}`).fontSize(30).onClick(() => {
360        this.val++;
361      })
362      Text(`AComp @Param val2: ${this.val2}`).fontSize(30)
363      A1Comp({ val: this.val })
364    }.border({ width: 2, color: Color.Green })
365  }
366}
367
368@ComponentV2
369struct A1Comp {
370  @Param val: number = 0;
371
372  build() {
373    Column() {
374      Text(`A1Comp @Param val ${this.val}`).fontSize(30)
375    }.border({ width: 2, color: Color.Pink })
376  }
377}
378```
379
380<!--no_check-->