1# Properly Choosing Between if/else and Visibility 2 3Both conditional rendering and the visibility attribute to switch components between visibility states. This document is intended to help you choose between these two approaches, by comparing their mechanism, usage scenarios, and performance in typical examples. 4 5## Mechanism 6 7### Conditional Rendering 8 9You can use the **if/else** statement for conditional rendering, one of the rendering control capabilities provided by the ArkUI application development framework. With conditional rendering, the UI description of a specific branch is rendered based on the application state. The mechanism of condition rendering is as follows: 10 11- During initial page construction, conditional statements are evaluated, components in the positive branch are built. If a positive branch is missing, no component is built. 12- When the application state changes, the conditional statements are re-evaluated. Components in the negative branch are deleted, and those in the positive branch are built. If a positive branch is missing, no component is built. 13 14For details about conditional rendering, see [if/else: Conditional Rendering](../quick-start/arkts-rendering-control-ifelse.md). 15 16### Visibility 17 18Visibility is one of the universal component attributes provided by ArkUI. You can set the visibility attribute of a component to show or hide the component. The table below describes the visibility attribute values. 19 20| Name | Description | 21| ------- | ---------------------------------------- | 22| Visible | The component is visible. | 23| Hidden | The component is invisible, but still takes up space in the layout. | 24| None | The component is invisible, and does not take up space in the layout.| 25 26For details about the visibility attribute, see [Visibility](../reference/apis-arkui/arkui-ts/ts-universal-attributes-visibility.md). 27 28### Mechanism Differences 29 30The table below compares the conditional rendering and visibility mechanisms. 31 32| Mechanism | Conditional Rendering| Visibility| 33| ------------------------------------------------------ | -------- | -------- | 34| Will a component be created if it is invisible during initial page construction? | No | Yes | 35| Will a component be destroyed and removed from the component tree if it changes from visible to invisible?| Yes | No | 36| Does a component takes up space in the layout when it is invisible? | No | Configurable| 37 38## Usage Scenarios 39 40Conditional rendering and the visibility attribute are applicable in different scenarios. 41 42Conditional rendering applies to the following scenarios: 43 44- To reduce render time and speed up application startup, consider conditional rendering in the cold start phase for components that do not need to be displayed initially when the application loads and draws the home page. 45- If a component does not frequently switch between visibility states, or does not need to be displayed most of the time, you are advised to use conditional rendering, so as to reduce GUI complexity, reduce nesting, and improve performance. 46- If memory is a critical concern, prefer conditional rendering for memory-hogging components, so they can be destroyed in time when they are not needed. 47- If the component subtree structure is complex and the conditions for rendering change frequently, it is recommended that you use conditional rendering with the component reuse mechanism to improve application performance. 48- If only some components need to be switched between visibility states and the conditions for rendering change frequently, combine conditional rendering with container restrictions to precisely control the component render scope to improve application performance. 49 50The visibility attribute applies to the following scenarios: 51 52- If a component is frequently switched between visibility states, the visibility attribute is a better choice, because it avoids frequent creation and destruction of the component and thereby improves performance. 53- If a component needs to take up space in the page layout when invisible, use the visibility attribute. 54 55### When Visibility Is Preferred 56 57The following exemplifies why the visibility attribute is a better choice for components that are frequently switched between visibility states. In the examples below, a button is used to show and hide 1000 images. 58 59**Nonexample** 60 61Use conditional statements to switch components between visibility states. 62 63```ts 64@Entry 65@Component 66struct WorseUseIf { 67 @State isVisible: boolean = true; 68 private data: number[] = []; 69 70 aboutToAppear() { 71 for (let i: number = 0; i < 1000; i++) { 72 this.data.push(i); 73 } 74 } 75 76 build() { 77 Column() { 78 Button("Switch visible and hidden").onClick(() => { 79 this.isVisible = !(this.isVisible); 80 }).width('100%') 81 Stack() { 82 if (this.isVisible) {// When conditional rendering is used, components are frequently created and destroyed. 83 Scroll() { 84 Column() { 85 ForEach(this.data, (item: number) => { 86 Image($r('app.media.icon')).width('25%').height('12.5%') 87 }, (item: number) => item.toString()) 88 } 89 } 90 } 91 } 92 } 93 } 94} 95``` 96 97**Example** 98 99Use the visibility attribute to switch components between visibility states. 100 101```ts 102@Entry 103@Component 104struct BetterUseVisibility { 105 @State isVisible: boolean = true; 106 private data: number[] = []; 107 108 aboutToAppear() { 109 for (let i: number = 0; i < 1000; i++) { 110 this.data.push(i); 111 } 112 } 113 114 build() { 115 Column() { 116 Button("Switch visible and hidden").onClick(() => { 117 this.isVisible = !(this.isVisible); 118 }).width('100%') 119 Stack() { 120 Scroll() { 121 Column() { 122 ForEach(this.data, (item: number) => { 123 Image($r('app.media.icon')).width('25%').height('12.5%') 124 }, (item: number) => item.toString()) 125 } 126 }.visibility(this.isVisible ? Visibility.Visible : Visibility.None)// When the visibility attribute is used, components are not frequently created and destroyed. 127 } 128 } 129 } 130} 131``` 132 133**Effect Comparison** 134 135Perform the same steps for the preceding example and nonexample: Click the button to hide the initially visible components, and then click the button again to show the components. Make sure the interval between showing and hiding the components is long enough for the page rendering to be complete. 136 137In the case of conditional rendering, components are destroyed when hidden, and re-created when shown. In this case, the core function **forEach** takes 1s, as shown below. 138 139 140 141The visibility attribute does not involve creation and destruction of components. Instead, it caches the components to the component tree, obtains the state value from the cache, and changes the value to switch the components between visibility states. In this case, the core function **forEach** takes 2 ms, as shown below. 142 143 144 145By comparing the performance data, we can learn that, regarding components that are frequently switched between visibility states, the visibility attribute outperforms conditional rendering, because it avoids frequent component creation and destruction. 146 147### When Conditional Rendering Is Preferred 148 149Conditional rendering applies in the cold start phase for components that do not need to be displayed initially when the application loads and draws the home page. In the examples below, there are invisible 1000 **Text** components at initial render. 150 151**Nonexample** 152 153Use the visibility attribute to hide components that do not need to be displayed at initial render of the home page. 154 155```ts 156@Entry 157@Component 158struct WorseUseVisibility { 159 @State isVisible: boolean = false; // The component is invisible during application startup. 160 private data: number[] = []; 161 162 aboutToAppear() { 163 for (let i: number = 0; i < 1000; i++) { 164 this.data.push(i); 165 } 166 } 167 168 build() { 169 Column() { 170 Button("Show the Hidden on start").onClick(() => { 171 this.isVisible = !(this.isVisible); 172 }).width('100%') 173 Stack() { 174 Image($r('app.media.icon')).objectFit(ImageFit.Contain).width('50%').height('50%') 175 Scroll() { 176 Column() { 177 ForEach(this.data, (item: number) => { 178 Text(`Item value: ${item}`).fontSize(20).width('100%').textAlign(TextAlign.Center) 179 }, (item: number) => item.toString()) 180 } 181 }.visibility(this.isVisible ? Visibility.Visible : Visibility.None)// When the visibility attribute is used, the component is created at application startup even when it is invisible. 182 } 183 } 184 } 185} 186``` 187 188**Example** 189 190Use conditional rendering to hide components that do not need to be displayed at initial render of the home page. 191 192```ts 193@Entry 194@Component 195struct BetterUseIf { 196 @State isVisible: boolean = false; // The component is invisible during application startup. 197 private data: number[] = []; 198 199 aboutToAppear() { 200 for (let i: number = 0; i < 1000; i++) { 201 this.data.push(i); 202 } 203 } 204 205 build() { 206 Column() { 207 Button("Show the Hidden on start").onClick(() => { 208 this.isVisible = !(this.isVisible); 209 }).width('100%') 210 Stack() { 211 Image($r('app.media.icon')).objectFit(ImageFit.Contain).width('50%').height('50%') 212 if (this.isVisible) { // When conditional rendering is used, the component is created at application startup when it is invisible. 213 Scroll() { 214 Column() { 215 ForEach(this.data, (item: number) => { 216 Text(`Item value: ${item}`).fontSize(20).width('100%').textAlign(TextAlign.Center) 217 }, (item: number) => item.toString()) 218 } 219 } 220 } 221 } 222 } 223 } 224} 225``` 226 227**Effect Comparison** 228 229Perform the same steps for the preceding example and nonexample: Run hdc commands to collect the CPU Profiler data of the main thread during the cold start of the application. For details, see [CPU Profiler](./application-performance-analysis.md#collecting-data-using-hdc-shell-commands). 230 231When the application loads and draws the home page during the cold start, using the visibility attribute creates the components that do not need to be displayed initially, which increases the render time. As shown below, in the UIAbility startup phase, it takes 401.1 ms to render the initial view with the visibility attribute. 232 233 234 235To reduce the render time during the cold start, use conditional rendering. In this case, components that do not need to be displayed initially will not be created. As shown below, in the UIAbility startup phase, it takes 12.6 ms to render the initial view with conditional rendering. 236 237 238 239By comparing the performance data, we can learn that, regarding components that do not need to be displayed during the application cold start, conditional rendering can reduce the render time and speed up application startup. 240 241### Conditional Rendering and Container Restrictions 242 243If only some components need to be switched between visibility states and the conditions for rendering change frequently, you can combine conditional rendering with container restrictions to precisely control the component render scope. The following exemplifies the effect of container restrictions in this usage scenario: A **Column** component is used to hold 1000 **Text** components, and one of the **Text** components is controlled with conditional rendering. 244 245**Nonexample** 246 247When conditional rendering is used without container restrictions, the container is created and destroyed upon condition changes, which causes all components in the container to be re-rendered. 248 249```ts 250@Entry 251@Component 252struct RenderControlWithoutStack { 253 @State isVisible: boolean = true; 254 private data: number[] = []; 255 256 aboutToAppear() { 257 for (let i: number = 0; i < 1000; i++) { 258 this.data.push(i); 259 } 260 } 261 262 build() { 263 Column() { 264 Stack() { 265 Scroll() { 266 Column() {// The re-render scope is extended to this layer. 267 if (this.isVisible) {// The container is created and destroyed upon condition changes, which causes all components in the container to be re-rendered. 268 Text('New item').fontSize(20) 269 } 270 ForEach(this.data, (item: number) => { 271 Text(`Item value: ${item}`).fontSize(20).width('100%').textAlign(TextAlign.Center) 272 }, (item: number) => item.toString()) 273 } 274 } 275 }.height('90%') 276 277 Button('Switch Hidden and Show').onClick(() => { 278 this.isVisible = !(this.isVisible); 279 }) 280 } 281 } 282} 283``` 284 285**Example** 286 287The following uses a container to limit the component render scope of conditional rendering. 288 289```ts 290@Entry 291@Component 292struct RenderControlWithStack { 293 @State isVisible: boolean = true; 294 private data: number[] = []; 295 296 aboutToAppear() { 297 for (let i: number = 0; i < 1000; i++) { 298 this.data.push(i); 299 } 300 } 301 302 build() { 303 Column() { 304 Stack() { 305 Scroll() { 306 Column() { 307 Stack() { // Set a layer of container for conditional rendering to limit the render scope. 308 if (this.isVisible) { 309 Text('New item').fontSize(20) 310 } 311 } 312 313 ForEach(this.data, (item: number) => { 314 Text(`Item value: ${item}`).fontSize(20).width('100%').textAlign(TextAlign.Center) 315 }, (item: number) => item.toString()) 316 } 317 } 318 }.height('90%') 319 320 Button('Switch Hidden and Show').onClick(() => { 321 this.isVisible = !(this.isVisible); 322 }) 323 } 324 } 325} 326``` 327 328**Effect Comparison** 329 330Perform the same steps for the preceding example and nonexample: Click the button to hide the initially visible **Text** component, and then click the button again to show the component. Make sure the interval between showing and hiding the components is long enough for the page rendering to be complete. 331 332The **Text** component in the container is contained in the **if** condition. Once the evaluation result of the **if** condition changes, the component is created or destroyed. In this case, the layout of the **Column** container is affected, and all components in the container, including the **ForEach** module, are re-rendered. As a result, the main thread spends a long time in UI re-rendering. 333 334The following figure shows the performance data when no container is used to limit the component render scope. The **Column** component is marked as a dirty area, and **ForEach** takes 13 ms. 335 336 337 338If a **Stack** component is used to wrap the **Text** component contained in the **if** condition, then only the **Text** component is re-rendered when the evaluation result of the **if** condition changes. This way, the UI re-rendering time of the main thread is reduced. 339 340The following figure shows the performance data when a container is used to limit the component render scope. The **Column** component is not marked as a dirty area, and no time is spent for **ForEach**. 341 342 343 344By comparing the performance data, we can conclude that, in scenarios where only some components need to be switched between visibility states and the conditions for rendering change frequently, combining conditional rendering with container restrictions can precisely control the component render scope and improve application performance. 345 346### Conditional Rendering and Component Reuse 347 348In scenarios where the branches of conditional rendering change repeatedly and the component subtree structure in each branch is complex, consider reusing components for conditional rendering. In the following examples, a custom complex child component **MockComplexSubBranch** is defined to work with conditional rendering. 349 350**Nonexample** 351 352Component reuse is not used to implement conditional rendering: 353 354```ts 355@Entry 356@Component 357struct IfWithoutReusable { 358 @State isAlignStyleStart: boolean = true; 359 360 build() { 361 Column() { 362 Button("Change FlexAlign").onClick(() => { 363 this.isAlignStyleStart = !this.isAlignStyleStart; 364 }) 365 Stack() { 366 if (this.isAlignStyleStart) { 367 MockComplexSubBranch({ alignStyle: FlexAlign.Start }); // MockComplexSubBranch is implemented without using the component reuse mechanism. 368 } else { 369 MockComplexSubBranch({ alignStyle: FlexAlign.End }); 370 } 371 } 372 } 373 } 374} 375``` 376 377The **MockComplexSubBranch** consists of three **Flex** container components and 200 **Text** components. It is used to simulate a complex subtree structure. The code snippet is as follows: 378 379```ts 380@Component 381export struct MockComplexSubBranch { 382 @State alignStyle: FlexAlign = FlexAlign.Center; 383 384 build() { 385 Column() { 386 Column({ space: 5 }) { 387 Text('ComplexSubBranch not reusable').fontSize(9).fontColor(0xCCCCCC).width('90%') 388 AlignContentFlex({ alignStyle: this.alignStyle }); 389 AlignContentFlex({ alignStyle: this.alignStyle }); 390 AlignContentFlex({ alignStyle: this.alignStyle }); 391 } 392 } 393 } 394} 395 396@Component 397struct AlignContentFlex { 398 @Link alignStyle: FlexAlign; 399 private data: number[] = []; 400 401 aboutToAppear() { 402 for (let i: number = 0; i < 200; i++) { 403 this.data.push(i); 404 } 405 } 406 407 build() { 408 Flex({ wrap: FlexWrap.Wrap, alignContent: this.alignStyle }) { 409 ForEach(this.data, (item: number) => { 410 Text(`${item % 10}`).width('5%').height(20).backgroundColor(item % 2 === 0 ? 0xF5DEB3 : 0xD2B48C) 411 }, (item: number) => item.toString()) 412 }.size({ width: '100%', height: 240 }).padding(10).backgroundColor(0xAFEEEE) 413 } 414} 415``` 416 417**Example** 418 419Component reuse is used to implement conditional rendering: 420 421```ts 422@Entry 423@Component 424struct IfWithReusable { 425 @State isAlignStyleStart: boolean = true; 426 427 build() { 428 Column() { 429 Button("Change FlexAlign").onClick(() => { 430 this.isAlignStyleStart = !this.isAlignStyleStart; 431 }) 432 Stack() { 433 if (this.isAlignStyleStart) { 434 MockComplexSubBranch({ alignStyle: FlexAlign.Start }); // MockComplexSubBranch is implemented using the component reuse mechanism. 435 } else { 436 MockComplexSubBranch({ alignStyle: FlexAlign.End }); 437 } 438 } 439 } 440 } 441} 442``` 443 444The implementation of **MockComplexSubBranch** is as follows. The **AlignContentFlex** code is the same as that in the preceding code snippet and therefore not included here. 445 446```ts 447@Component 448@Reusable // Add the @Reusable decorator to declare that the component can be reused. 449export struct MockComplexSubBranch { 450 @State alignStyle: FlexAlign = FlexAlign.Center; 451 452 aboutToReuse(params: ESObject) {// Called when the component is about to be added from the reuse cache to the component tree. 453 this.alignStyle = params.alignStyle; 454 } 455 456 build() { 457 Column() { 458 Column({ space: 5 }) { 459 Text('ComplexSubBranch reusable').fontSize(9).fontColor(0xCCCCCC).width('90%') 460 AlignContentFlex({ alignStyle: this.alignStyle }); 461 AlignContentFlex({ alignStyle: this.alignStyle }); 462 AlignContentFlex({ alignStyle: this.alignStyle }); 463 } 464 } 465 } 466} 467 468``` 469 470**Effect Comparison** 471 472Perform the same steps for the preceding example and nonexample: Click the button to change alignment of the **Text** component in the **Flex** container along the main axis. Make sure the interval between alignment switching is long enough for the page rendering to be complete. 473 474The subtree structure of the **MockComplexSubBranch** component in each branch is complex. In this case, when the button is repeatedly clicked to change the branch, a large number of components are destroyed and created. As a result, if components are not reused, UI re-rendering can be significantly time-consuming. As shown below, the rendering of the application **Index** page takes 180 ms. 475 476 477 478If components are reused during conditional rendering, the UI re-rendering time can be greatly shortened. As shown below, the rendering of the application **Index** page takes as short as 14 ms. 479 480 481 482In sum, if the component subtree structure is complex and the conditions for rendering change frequently, it is recommended that you use conditional rendering with the component reuse mechanism to improve application performance. 483