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![img](./figures/WorseUseIf.png)
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![img](./figures/BetterUseVisibility.png)
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![img](./figures/WorseUseVisibility.png)
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![img](./figures/BetterUseIf.png)
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![img](./figures/RenderControlWithoutStack.png)
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![img](./figures/RenderControlWithStack.png)
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![img](./figures/IfWithoutReusable.png)
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![img](./figures/IfWithReusable.png)
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