1# \@LocalBuilder Decorator: Maintaining the Parent-Child Relationship Between Component and State Management 2 3When use @Builder to pass data, the parent-child relationship of components is considered. After **bind(this)** is used, the parent-child relationship of components is inconsistent with that of state management. As a result, the @LocalBuilder decorator is used to fix the inconsistency. @LocalBuilder has the same features as local @Builder and provides a better determination of the parent-child relationship of components and state management. 4 5 6> **NOTE** 7> 8> This decorator is supported since API version 12. 9> 10 11## How to Use 12 13 14### Local Custom Builder Function 15 16Syntax: 17 18 19```ts 20@LocalBuilder MyBuilderFunction() { ... } 21``` 22 23Usage: 24 25 26```ts 27this.MyBuilderFunction() 28``` 29 30- One or more @LocalBuilder methods can be defined in a custom component. The methods are considered as private and special member functions of the component. 31- The custom builder function can be called from the **build** method or another custom builder function in the same component only. 32- Inside the custom builder function body, **this** refers to the owning component. Component state variables are accessible from within the custom builder function implementation. Using **this** to access the custom components' state variables is recommended over parameter passing. 33 34## Constraints 35 36- @LocalBuilder can be declared only within the component to which it belongs. Global declaration is not allowed. 37 38- @LocalBuilder cannot be used by built-in decorators and custom decorators. 39 40- Static methods in a custom component cannot be used together with @LocalBuilder. 41 42## Differences Between @LocalBuilder and Local @Builder 43 44To change the pointed object of **this**, **bind(this)** used in the local @Builder will cause inconsistent parent-child relationship between the component and the state management. However, this problem does not exist in the @LocalBuilder. For details, see [Differences between @LocalBuilder and @Builder](arkts-localBuilder.md#differences-between-localbuilder-and-builder). 45 46## Parameter Passing Rules 47 48For @LocalBuilder functions, parameters can be passed [by value](#by-value-parameter-passing) and [by reference](#by-reference-parameter-passing). Both of them must comply with the following rules: 49 50- The parameter type must be the same as the declared parameter type. The **undefined** or **null** constants as well as expressions evaluating to these values are not allowed. 51 52- All parameters must be immutable inside the @LocalBuilder function. 53 54- The \@LocalBuilder function body follows the same [syntax rules](arkts-create-custom-components.md#build-function) as the **build()** function. 55 56- Parameters are passed by value in all cases except when only one parameter is passed in and the parameter needs to be directly passed to the object literal. 57 58 59### By-Reference Parameter Passing 60 61In by-reference parameter passing, state variables can be passed, and the change of these state variables causes the UI re-rendering in the \@LocalBuilder decorated method. 62 63Use scenario: 64 65The @LocalBuilder method in the **Parent** component is called in the **build** function to pass the parameters by keys. When you click **Click me**, the **Text** content in the @LocalBuilder changes with the state variable. 66 67```ts 68class ReferenceType { 69 paramString: string = ''; 70} 71 72@Entry 73@Component 74struct Parent { 75 @State variableValue: string = 'Hello World'; 76 77 @LocalBuilder 78 citeLocalBuilder(params: ReferenceType) { 79 Row() { 80 Text(`UseStateVarByReference: ${params.paramString} `) 81 } 82 }; 83 84 build() { 85 Column() { 86 this.citeLocalBuilder({ paramString: this.variableValue }); 87 Button('Click me').onClick(() => { 88 this.variableValue = 'Hi World'; 89 }) 90 } 91 } 92} 93``` 94 95When parameters are passed by reference, if a custom component is called within the\@LocalBuilder method, ArkUI provides [$$](arkts-two-way-sync.md) as the paradigm for passing parameters by reference. 96 97Use scenario: 98 99The @LocalBuilder method in the **Parent** component is called in the custom component to pass the parameters by reference. When the value of a state variable in the **Parent** component changes, the **message** of the custom component **HelloComponent** in the @LocalBuilder method also changes. 100 101```ts 102class ReferenceType { 103 paramString: string = ''; 104} 105 106@Component 107struct HelloComponent { 108 @Prop message: string; 109 110 build() { 111 Row() { 112 Text(`HelloComponent===${this.message}`); 113 } 114 } 115} 116 117@Entry 118@Component 119struct Parent { 120 @State variableValue: string = 'Hello World'; 121 122 @LocalBuilder 123 citeLocalBuilder($$: ReferenceType) { 124 Row() { 125 Column() { 126 Text(`citeLocalBuilder===${$$.paramString}`); 127 HelloComponent({ message: $$.paramString }); 128 } 129 } 130 } 131 132 build() { 133 Column() { 134 this.citeLocalBuilder({ paramString: this.variableValue }); 135 Button('Click me').onClick(() => { 136 this.variableValue = 'Hi World'; 137 }) 138 } 139 } 140} 141``` 142 143 144### By-Value Parameter Passing 145 146By default, parameters in the \@LocalBuilder decorated functions are passed by value. In this case, when the passed parameter is a state variable, the change of the state variable does not cause UI re-rendering in the \@LocalBuilder decorated function. Therefore, when passing state variables, you are advised to use [by-reference parameter passing](#by-reference-parameter-passing). 147 148Use scenario: 149 150The **Parent** component passes the @State decorated **label** value to the @LocalBuilder function as a function parameter. In this case, the value obtained by the @LocalBuilder function is a common variable value. Therefore, when the @State decorated **label** value is changed, the value in the @LocalBuilder function does not change. 151 152 153```ts 154@Entry 155@Component 156struct Parent { 157 @State label: string = 'Hello'; 158 159 @LocalBuilder 160 citeLocalBuilder(paramA1: string) { 161 Row() { 162 Text(`UseStateVarByValue: ${paramA1} `) 163 } 164 } 165 166 build() { 167 Column() { 168 this.citeLocalBuilder(this.label); 169 } 170 } 171} 172``` 173 174## Differences Between @LocalBuilder and @Builder 175 176When the **componentBuilder** function is decorated by @Builder, the **Child** component is displayed. When the **componentBuilder** function is decorated by @LocalBuilder, **Parent** component is displayed. 177 178**NOTE** 179 180**@Builder componentBuilder()** is passed to the child component **@BuilderParam customBuilderParam** in the form of **this.componentBuilder**. **this** points to the label of **Child**, that is, **Child** is displayed. 181 182**@LocalBuilder componentBuilder()** is passed to the child component **@BuilderParam customBuilderParam** in the form of **this.componentBuilder**. **this** points to the label of **Parent**, that is, **Parent** is displayed. 183 184```ts 185@Component 186struct Child { 187 label: string = `Child`; 188 @BuilderParam customBuilderParam: () => void; 189 190 build() { 191 Column() { 192 this.customBuilderParam() 193 } 194 } 195} 196 197@Entry 198@Component 199struct Parent { 200 label: string = `Parent`; 201 202 @Builder componentBuilder() { 203 Text(`${this.label}`) 204 } 205 206 // @LocalBuilder componentBuilder() { 207 // Text(`${this.label}`) 208 // } 209 210 build() { 211 Column() { 212 Child({ customBuilderParam: this.componentBuilder }) 213 } 214 } 215} 216``` 217 218## Use Scenarios 219 220### Using @LocalBuilder in @ComponentV2 Decorated Custom Components 221 222Call the @LocalBuilder in the custom component decorated by @ComponentV2 to change the variables, triggering the UI re-renders. 223 224```ts 225@ObservedV2 226class Info { 227 @Trace name: string = ''; 228 @Trace age: number = 0; 229} 230 231@ComponentV2 232struct ChildPage { 233 @Require @Param childInfo: Info; 234 build() { 235 Column() { 236 Text(`Custom component name :${this.childInfo.name}`) 237 .fontSize(20) 238 .fontWeight(FontWeight.Bold) 239 Text(`Custom component age :${this.childInfo.age}`) 240 .fontSize(20) 241 .fontWeight(FontWeight.Bold) 242 } 243 } 244} 245 246@Entry 247@ComponentV2 248struct ParentPage { 249 info1: Info = { name: "Tom", age: 25 }; 250 @Local info2: Info = { name: "Tom", age: 25 }; 251 252 @LocalBuilder 253 privateBuilder() { 254 Column() { 255 Text(`LocalBuilder@Builder name :${this.info1.name}`) 256 .fontSize(20) 257 .fontWeight(FontWeight.Bold) 258 Text(`LocalBuilder@Builder age :${this.info1.age}`) 259 .fontSize(20) 260 .fontWeight(FontWeight.Bold) 261 } 262 } 263 264 @LocalBuilder 265 privateBuilderSecond() { 266 Column() { 267 Text(`LocalBuilder@Builder name :${this.info2.name}`) 268 .fontSize(20) 269 .fontWeight(FontWeight.Bold) 270 Text(`LocalBuilder@Builder age :${this.info2.age}`) 271 .fontSize(20) 272 .fontWeight(FontWeight.Bold) 273 } 274 } 275 build() { 276 Column() { 277 Text(`info1: ${this.info1.name} ${this.info1.age}`) // Text1 278 .fontSize(30) 279 .fontWeight(FontWeight.Bold) 280 this.privateBuilder() // Call the local @Builder. 281 Line() 282 .width('100%') 283 .height(10) 284 .backgroundColor('#000000').margin(10) 285 Text(`info2: ${this.info2.name} ${this.info2.age}`) // Text2 286 .fontSize(30) 287 .fontWeight(FontWeight.Bold) 288 this.privateBuilderSecond() // Call the local @Builder. 289 Line() 290 .width('100%') 291 .height(10) 292 .backgroundColor('#000000').margin(10) 293 Text(`info1: ${this.info1.name} ${this.info1.age}`) // Text1 294 .fontSize(30) 295 .fontWeight(FontWeight.Bold) 296 ChildPage({childInfo: this.info1}) // Call the custom component. 297 Line() 298 .width('100%') 299 .height(10) 300 .backgroundColor('#000000').margin(10) 301 Text(`info2: ${this.info2.name} ${this.info2.age}`) // Text2 302 .fontSize(30) 303 .fontWeight(FontWeight.Bold) 304 ChildPage({childInfo: this.info2}) // Call the custom component. 305 Line() 306 .width('100%') 307 .height(10) 308 .backgroundColor('#000000').margin(10) 309 Button("change info1&info2") 310 .onClick(() => { 311 this.info1 = { name: "Cat", age: 18} // Text1 is not re-rendered because no decorator is used to listen for value changes. 312 this.info2 = { name: "Cat", age: 18} // Text2 is re-rendered because a decorator is used to listen for value changes. 313 }) 314 } 315 } 316} 317``` 318