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
11## Overview
12
13\@Provider provides data. Its child components can use the \@Consumer to obtain the data by binding the same **key**.
14\@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.
15Data types decorated by \@Provider and \@Consumer must be the same.
16
17
18The following notes must be paid attention to when using \@Provider and \@Consumer:
19- \@Provider and \@Consumer strongly depend on the custom component levels. \@Consumer is initialized to different values because the parent component of the custom component is different.
20- Using \@Provider with \@Consumer is equivalent to bonding components together. From the perspective of independent component, usage of \@Provider and \@Consumer should be lessened.
21
22
23## Capability Comparison: \@Provider and \@Consumer Vs. \@Provide and \@Consume
24In 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.
25If you are not familiar with \@Provide and \@Consume in state management V1, please skip this section.
26
27| Capability| \@Provider and \@Consumer Decorators of V2                                            |\@Provide and \@Consume Decorators of V1|
28| ------------------ | ----------------------------------------------------- |----------------------------------------------------- |
29| \@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.|
30| Supported type          | **function** is supported.| **function** is not supported.|
31| 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).|
32| 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.|
33| \@Provide(r) initialization from the parent component     | Forbidden.| Allowed.|
34| \@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**.|
35
36
37## Decorator Description
38
39### Basic Rules
40\@Provider syntax:
41@Provider(alias?: string) varName : varType = initValue
42
43| \@Provider Property Decorator| Description                                                 |
44| ------------------ | ----------------------------------------------------- |
45| Decorator parameters        | **aliasName?: string**: alias. The default value is the attribute name.|
46| Supported type          | Member variables in the custom component.<br>Property types include number, string, boolean, class, Array, Date, Map, and Set.<br>[Arrow function](#decorating-callback-by-using-provider-and-consumer-and-facilitating-behavior-abstraction-between-components). |
47| Initialization from the parent component     | Forbidden.|
48| Local initialization        | Required.|
49| Observation capability        | Be equivalent to \@Trace. Changes will be synchronized to the corresponding \@Consumer.|
50
51\@Consumer syntax:
52@Consumer(alias?: string) varName : varType = initValue
53
54
55| \@Consumer Property Decorator| Description                                                        |
56| --------------------- | ------------------------------------------------------------ |
57| Decorator parameters           | **aliasName?: string**: alias. The default value is the attribute name. The nearest \@Provider is searched upwards.   |
58| Supported type | Member variables in the custom component.<br>Property types include number, string, boolean, class, Array, Date, Map, and Set.<br>Arrow function. |
59| Initialization from the parent component     | Forbidden.|
60| Local initialization        | Required.|
61| Observation capability        | Be equivalent to \@Trace. Changes will be synchronized to the corresponding \@Provider.|
62
63### aliasName and Attribute Name
64\@Provider and \@Consumer accept the optional parameter **aliasName**. If the parameter is not set, the attribute name will be used as the default **aliasName** Note that **aliasName** is the unique key used to match \@Provider and \@Consumer.
65
66The following three examples clearly describe how \@Provider and \@Consumer use **aliasName** for searching and matching.
67```ts
68@ComponentV2
69struct Parent {
70  // The aliasName is not defined. Use the property name "str" as the aliasName.
71  @Provider() str: string = 'hello';
72}
73
74@ComponentV2
75struct Child {
76  // Define aliasName as"str" and use it to search.
77  // If the value can be found on the Parent component, use the value "hello" of @Provider.
78  @Consumer('str') str: string = 'world';
79}
80```
81
82```ts
83@ComponentV2
84struct Parent {
85  // Define aliasName as "alias".
86  @Provider('alias') str: string = 'hello';
87}
88
89@ComponentV2 struct Child {
90  // Define aliasName as "alias", find out @Provider, and obtain the value "hello".
91  @Consumer('alias') str: string = 'world';
92}
93```
94
95```ts
96@ComponentV2
97struct Parent {
98  // Define aliasName as "alias".
99  @Provider('alias') str: string = 'hello';
100}
101
102@ComponentV2
103struct Child {
104  // The aliasName is not defined. Use the property name "str" as the aliasName.
105  // The corresponding @Provider is not found, use the local value "world".
106  @Consumer() str: string = 'world';
107}
108```
109
110## Variable Passing
111
112| Passing Rules      | Description                                                        |
113| -------------- | ------------------------------------------------------------ |
114| Initialization from the parent component| Variables decorated by \@Provider and \@Consumer can only be initialized locally.|
115| Child component initialization  | Variables decorated by \@Provider and \@Consumer can be used to initialize \@Param decorated variables in the child component.|
116
117## Constraints
118
1191. \@Provider and \@Consumer are property decorators for custom components. They can only decorate the attributes of custom components and cannot decorate the class properties.
1202. \@Provider and \@Consumer are decorators of the state management V2, which can be used only in \@ComponentV2 but not in \@Component.
1213. \@Provider and \@Consumer support only local initialization.
122
123## Use Scenarios
124
125### Synchronizing \@Provider and \@Consumer in a Two-Way Manner
126#### Establishing a Two-Way Binding
1271. Initialize the **Parent** and **Child** custom components:
128    - **@Consumer() str: string = 'world'** in the **Child** component searches upwards to find **@Provider() str: string = 'hello'** in the **Parent** component.
129    - **@Consumer() str: string = 'world'** is initialized to the value of **@Provider**, that is, **'hello'**.
130    - Both of them establish a two-way synchronization relationship.
1312. Click the button in **Parent** to change the \@Provider decorated **str** and notify the corresponding \@Consumer to re-render the UI.
1323. Click the button in **Child** to change the \@Consumer decorated **str**, and notify the corresponding \@Provider to re-render the UI.
133
134```ts
135@Entry
136@ComponentV2
137struct Parent {
138  @Provider() str: string = 'hello';
139
140  build() {
141    Column() {
142      Button(this.str)
143        .onClick(() => {
144          this.str += '0';
145        })
146      Child()
147    }
148  }
149}
150
151@ComponentV2
152struct Child {
153  @Consumer() str: string = 'world';
154
155  build() {
156    Column() {
157      Button(this.str)
158        .onClick(() => {
159          this.str += '0';
160        })
161    }
162  }
163}
164```
165#### Establishing a Two-Way Binding Failed
166
167In the following example, \@Provider and \@Consumer fail to establish a two-way synchronization relationship because of different **aliasName** value.
1681. Initialize the **Parent** and **Child** custom components:
169    - @Provider is not found when **@Consumer() str: string = 'world'** in the **Child** component searches upwards.
170    - **@Consumer() str: string = 'world'** uses the local default value 'world'.
171    - Both of them fail to establish a two-way synchronization relationship.
1722. Click the button in the **Parent** component to change @Provider decorated **str1** and re-render only the **Button** component associated with @Provider.
1733. Click the button in the **Child** component to change the @Consumer decorated **str** and re-render only the **Button** component associated with @Consumer.
174
175```ts
176@Entry
177@ComponentV2
178struct Parent {
179  @Provider() str1: string = 'hello';
180
181  build() {
182    Column() {
183      Button(this.str1)
184        .onClick(() => {
185          this.str1 += '0';
186        })
187      Child()
188    }
189  }
190}
191
192@ComponentV2
193struct Child {
194  @Consumer() str: string = 'world';
195
196  build() {
197    Column() {
198      Button(this.str)
199        .onClick(() => {
200          this.str += '0';
201        })
202    }
203  }
204}
205```
206
207### Decorating Callback by Using @Provider and @Consumer and Facilitating Behavior Abstraction Between Components
208
209To register a callback function for a child component in a parent component, you can use \@Provider and \@Consumer to decorate a callback.
210For example, when a drag event occurs, if you want to synchronize the start position of the child component to the parent component, see the example below.
211
212```ts
213@Entry
214@ComponentV2
215struct Parent {
216  @Local childX: number = 0;
217  @Local childY: number = 1;
218  @Provider() onDrag: (x: number, y: number) => void = (x: number, y: number) => {
219    console.log(`onDrag event at x=${x} y:${y}`);
220    this.childX = x;
221    this.childY = y;
222  }
223
224  build() {
225    Column() {
226      Text(`child position x: ${this.childX}, y: ${this.childY}`)
227      Child()
228    }
229  }
230}
231
232@ComponentV2
233struct Child {
234  @Consumer() onDrag: (x: number, y: number) => void = (x: number, y: number) => {};
235
236  build() {
237    Button("changed")
238      .draggable(true)
239      .onDragStart((event: DragEvent) => {
240        // Current Previewer does not support common drag events.
241        this.onDrag(event.getDisplayX(), event.getDisplayY());
242      })
243  }
244}
245```
246
247
248### Decorating Complex Types by \@Provider and \@Consumer and Using together with \@Trace
249
2501. \@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 properties, \@Trace is also required.
2512. When decorating built-in types, such as Array, Map, Set, and Date, you can observe the changes of some APIs. The observation capability is the same as that of [\@Trace](./arkts-new-observedV2-and-trace.md#observed-changes).
252
253```ts
254@ObservedV2
255class User {
256  @Trace name: string;
257  @Trace age: number;
258
259  constructor(name: string, age: number) {
260    this.name = name;
261    this.age = age;
262  }
263}
264const data: User[] = [new User('Json', 10), new User('Eric', 15)];
265@Entry
266@ComponentV2
267struct Parent {
268  @Provider('data') users: User[] = data;
269
270  build() {
271    Column() {
272      Child()
273      Button('add new user')
274        .onClick(() => {
275          this.users.push(new User('Molly', 18));
276        })
277      Button('age++')
278        .onClick(() => {
279          this.users[0].age++;
280        })
281      Button('change name')
282        .onClick(() => {
283          this.users[0].name = 'Shelly';
284        })
285    }
286  }
287}
288
289@ComponentV2
290struct Child {
291  @Consumer('data') users: User[] = [];
292
293  build() {
294    Column() {
295      ForEach(this.users, (item: User) => {
296        Column() {
297          Text(`name: ${item.name}`).fontSize(30)
298          Text(`age: ${item.age}`).fontSize(30)
299          Divider()
300        }
301      })
302    }
303  }
304}
305```
306
307### Searching Upwards by \@Consumer for the Nearest \@Provider
308If \@Provider has duplicate names in the component tree, \@Consumer will search upwards for the \@Provider data of the nearest parent node.
309```ts
310@Entry
311@ComponentV2
312struct Parent {
313  @Provider() val: number = 10;
314
315  build() {
316    Column() {
317      AComp()
318    }
319  }
320}
321
322@ComponentV2
323struct AComp {
324  @Provider() val: number = 20;
325  @Consumer("val") val2: number = 0; // 10
326
327  build() {
328    Column() {
329      Text(`${this.val2}`)
330      A1Comp()
331    }
332  }
333}
334
335@ComponentV2
336struct A1Comp {
337  @Consumer() val: number = 0; // 20
338
339  build() {
340    Column() {
341      Text(`${this.val}`)
342    }
343  }
344}
345```
346
347In the preceding example:
348
349- In **AComp**, \@Consumer searches upwards to find **@Provider() val: number = 10** defined in the **Parent** component. Therefore, the value is initialized to 10.
350- 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.
351
352### Initializing \@Param by \@Provider and \@Consumer
353
354- 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 re-render of the **Text(@Param val2: ${this.val2})** in the **Child** component.
355- The change of **@Consumer() val** is also synchronized to **A1Comp**, triggering the re-render of **Text(A1Comp @Param val ${this.val})**.
356
357```ts
358@Entry
359@ComponentV2
360struct Parent {
361  @Provider() val: number = 10;
362
363  build() {
364    Column() {
365      AComp({ val2: this.val })
366    }
367  }
368}
369
370@ComponentV2
371struct AComp {
372  @Consumer() val: number = 0;
373  @Param val2: number = 0;
374
375  build() {
376    Column() {
377      Text(`AComp @Consumer val: ${this.val}`).fontSize(30).onClick(() => {
378        this.val++;
379      })
380      Text(`AComp @Param val2: ${this.val2}`).fontSize(30)
381      A1Comp({ val: this.val })
382    }.border({ width: 2, color: Color.Green })
383  }
384}
385
386@ComponentV2
387struct A1Comp {
388  @Param val: number = 0;
389
390  build() {
391    Column() {
392      Text(`A1Comp @Param val ${this.val}`).fontSize(30)
393    }.border({ width: 2, color: Color.Pink })
394  }
395}
396```
397
398<!--no_check-->