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