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-->