目录

if/else: Conditional Rendering

ArkTS supports conditional rendering, allowing you to display different content based on the application state using if, else, and else if statements.

This API can be used in ArkTS widgets since API version 9.

Usage Rules

Update Mechanism

A conditional statement updates whenever a state variable used inside the if or else if condition changes. Specifically:

  1. The conditional statement re-evaluates the conditions. If the evaluation of the conditions changes, steps 2 and 3 are performed. Otherwise, no follow-up operation is required.

  2. All previously constructed child components within the conditional branch are removed from the component tree.

  3. The builder function of the new branch is executed, and the generated child components are added to the if parent component. If no applicable else branch exists, nothing is created.

Conditions can contain TypeScript expressions. Expressions within builder functions must not modify application state.

Use Cases

[h2]Basic Conditional Rendering with if

@Entry
@Component
struct IfExample {
  @State count: number = 0;

  build() {
    Column() {
      Text(`count=${this.count}`)

      if (this.count > 0) {
        Text(`count is positive`)
          .fontColor(Color.Green)
      }

      Button('increase count')
        .onClick(() => {
          this.count++;
        })

      Button('decrease count')
        .onClick(() => {
          this.count--;
        })
    }
  }
}

Each branch of the if statement includes a build function. Each of such build functions must create one or more components. On initial render, if will execute a build function and add the generated child component to its parent component.

if updates whenever a state variable used inside the if or else if condition changes, and re-evaluates the conditions. If the evaluation of the conditions changes, it means that another branch of if needs to be built. In this case, the ArkUI framework will:

  1. Remove all previously rendered components from the previous branch.

  2. The builder function of the new branch is executed, and the generated child components are added to their parent component.

In the preceding example, when the value of count increases from 0 to 1, the condition if (this.count > 0) becomes true. The builder function of this branch is executed, creating a Text component and adding it to the parent Column component. If count subsequently changes to 0, the Text component is removed from the Column component. Because there is no else branch, no new builder function is executed.

[h2]if/ else Statements and Child Component States

This example involves an if/else statement and a child component with a @State decorated variable.

@Component
struct CounterView {
  @State counter: number = 0;
  label: string = 'unknown';

  build() {
    Column({ space: 20 }) {
      Text(`${this.label}`)
      Button(`counter ${this.counter} +1`)
        .onClick(() => {
          this.counter += 1;
        })
    }
    .margin(10)
    .padding(10)
    .border({ width: 1 })
  }
}

@Entry
@Component
struct MainView {
  @State toggle: boolean = true;

  build() {
    Column() {
      if (this.toggle) {
        CounterView({ label: 'CounterView #positive' });
      } else {
        CounterView({ label: 'CounterView #negative' });
      }
      Button(`toggle ${this.toggle}`)
        .onClick(() => {
          this.toggle = !this.toggle;
        })
    }
    .width('100%')
    .justifyContent(FlexAlign.Center)
  }
}

Initial rendering: creates CounterView child component with label 'CounterView #positive' and initial counter value 0.

Modification to the counter variable of CounterView: re-renders the existing CounterView component (with label 'CounterView #positive') while preserving the state variable value.

MainView.toggle change to false: updates the if statement, causing the following:

  1. Removal of the old CounterView instance (label: 'CounterView #positive').
  2. Creation of a new CounterView instance (label: 'CounterView #negative') with counter reset to 0.

CounterView(label: 'CounterView #positive') and CounterView(label: 'CounterView #negative') are two distinct instances of the same custom component. When the if branch changes, there is no update to an existing child component or no preservation of state.

The following example shows the required modifications if the value of counter needs to be preserved when the if condition changes:

@Component
struct KeepCounterView {
  @Link counter: number;
  label: string = 'unknown';

  build() {
    Column({ space: 20 }) {
      Text(`${this.label}`)
        .fontSize(20)
      Button(`counter ${this.counter} +1`)
        .onClick(() => {
          this.counter += 1;
        })
    }
    .margin(10)
    .padding(10)
    .border({ width: 1 })
  }
}

@Entry
@Component
struct KeepMainView {
  @State toggle: boolean = true;
  @State counter: number = 0;

  build() {
    Column() {
      if (this.toggle) {
        KeepCounterView({ counter: $counter, label: 'CounterView #positive' });
      } else {
        KeepCounterView({ counter: $counter, label: 'CounterView #negative' });
      }
      Button(`toggle ${this.toggle}`)
        .onClick(() => {
          this.toggle = !this.toggle;
        })
    }
    .width('100%')
    .justifyContent(FlexAlign.Center)
  }
}

Here, the @State decorated variable counter is owned by the parent component. Therefore, it is not destroyed when the KeepCounterView component instance is destroyed. The KeepCounterView component references the state through the @Link decorator. The state must be moved from a child to its parent (or parent of parent) to avoid losing it when the conditional content (or repeated content) is destroyed.

[h2]Nested Conditional Statements

Nested conditional statements maintain the same rendering rules without affecting parent component constraints.

@Entry
@Component
struct NestedIf {
  @State toggle: boolean = false;
  @State toggleColor: boolean = false;

  build() {
    Column({ space: 20 }) {
      Text('Before')
        .fontSize(15)
      if (this.toggle) {
        Text('Top True, positive 1 top')
          .backgroundColor('#aaffaa').fontSize(20)
        // Inner if statement
        if (this.toggleColor) {
          Text('Top True, Nested True, positive COLOR  Nested ')
            .backgroundColor('#00aaaa').fontSize(15)
        } else {
          Text('Top True, Nested False, Negative COLOR  Nested ')
            .backgroundColor('#aaaaff').fontSize(15)
        }
      } else {
        Text('Top false, negative top level').fontSize(20)
          .backgroundColor('#ffaaaa')
        if (this.toggleColor) {
          Text('positive COLOR  Nested ')
            .backgroundColor('#00aaaa').fontSize(15)
        } else {
          Text('Negative COLOR  Nested ')
            .backgroundColor('#aaaaff').fontSize(15)
        }
      }
      Text('After')
        .fontSize(15)
      Button('Toggle Outer')
        .onClick(() => {
          this.toggle = !this.toggle;
        })
      Button('Toggle Inner')
        .onClick(() => {
          this.toggleColor = !this.toggleColor;
        })
    }
    .width('100%')
    .justifyContent(FlexAlign.Center)
  }
}