sroxck

sroxck

ArkTs Component Basics

In ArkUI, the content displayed in the UI consists of components, with those provided directly by the framework referred to as system components, and those defined by developers referred to as custom components. When developing UI interfaces, it is usually not a simple matter of combining system components; rather, factors such as code reusability, separation of business logic and UI, and future version evolution need to be considered. Therefore, encapsulating the UI and some business logic into custom components is an indispensable capability.

Custom Components#

Custom components have the following characteristics:

  • Composable: Allows developers to combine system components along with their properties and methods.
  • Reusable: Custom components can be reused by other components and used as different instances in different parent components or containers.
  • Data-driven UI updates: UI refresh is driven by changes in state variables.

Basic Usage of Custom Components#

@Component
struct HelloComponent {
  @State message: string = 'Hello, World!';

  build() {
    // HelloComponent custom component combines system components Row and Text
    Row() {
      Text(this.message)
        .onClick(() => {
          // The change of the state variable message drives the UI refresh, updating the UI from 'Hello, World!' to 'Hello, ArkUI!'
          this.message = 'Hello, ArkUI!';
        })
    }
  }
}

::: warning Note

If this custom component is referenced in another file, it needs to be exported using the export keyword, and the custom component should be imported on the page where it is used.
:::

HelloComponent can be created multiple times in the build() function of other custom components, achieving the reuse of custom components.

class HelloComponentParam {
  message: string = ""
}

@Entry
@Component
struct ParentComponent {
  param: HelloComponentParam = {
    message: 'Hello, World!'
  }

  build() {
    Column() {
      Text('ArkUI message')
      HelloComponent(this.param);
      Divider()
      HelloComponent(this.param);
    }
  }
}

Basic Structure of Custom Components#

  • struct: Custom components are implemented based on struct, with the combination of struct + custom component name + {...} forming the custom component, and there can be no inheritance relationship. Instantiation of struct can omit new.

::: tip Note
Custom component names, class names, and function names cannot be the same as system component names.
:::

  • @Component: The @Component decorator can only decorate data structures declared with the struct keyword. After being decorated with @Component, a struct gains component capabilities and needs to implement the build method to describe the UI; a struct can only be decorated by one @Component. @Component can accept an optional boolean parameter.

::: tip Note
Starting from API version 9, this decorator supports use in ArkTS cards.

Starting from API version 11, @Component can accept an optional boolean parameter.
:::

@Component
struct MyComponent {
}

freezeWhenInactive#

Component freeze option.

@Component({ freezeWhenInactive: true })
struct MyComponent {
}

build() Function#

The build() function is used to define the declarative UI description of the custom component, and the custom component must define the build() function.

@Component
struct MyComponent {
  build() {
  }
}

@Entry#

Custom components decorated with @Entry will serve as the entry point for the UI page. In a single UI page, at most one custom component can be decorated with @Entry. @Entry can accept an optional LocalStorage parameter.

::: tip Note
Starting from API version 9, this decorator supports use in ArkTS cards.

Starting from API version 10, @Entry can accept an optional LocalStorage parameter or an optional EntryOptions parameter.

Starting from API version 11, this decorator supports use in meta services.
:::

@Entry
@Component
struct MyComponent {
}```

EntryOptions10#

Named route jump options.

@Entry({ routeName : 'myPage' })
@Component
struct MyComponent {
}

@Reusable: Custom components decorated with @Reusable have reuse capabilities.

Member Functions/Variables#

  • In addition to needing to implement the build() function, custom components can also implement other member functions, which have the following constraints:
  • Member functions of custom components are private and should not be declared as static functions.
  • Custom components can contain member variables, which have the following constraints:
  • Member variables of custom components are private and should not be declared as static variables.
  • Local initialization of member variables in custom components is sometimes optional and sometimes required. For specifics on whether local initialization is needed or whether member variables of child components need to be initialized through parameters from parent components, please refer to state management.

Parameter Specifications for Custom Components#

From the examples above, we have learned that custom components can be created in the build method, and during the creation of custom components, parameters can be initialized according to the rules of the decorators.

@Component
struct MyComponent {
  private countDownFrom: number = 0;
  private color: Color = Color.Blue;

  build() {
  }
}

@Entry
@Component
struct ParentComponent {
  private someColor: Color = Color.Pink;

  build() {
    Column() {
      // Create an instance of MyComponent, initializing the member variable countDownFrom to 10 and the member variable color to this.someColor
      MyComponent({ countDownFrom: 10, color: this.someColor })
    }
  }
}

build() Function#

All statements declared in the build() function are collectively referred to as UI descriptions, which need to follow the following rules:

  • For custom components decorated with @Entry, the root node under the build() function must be unique and necessary, and must be a container component, where ForEach is prohibited as the root node.
  • For custom components decorated with @Component, the root node under the build() function must be unique and necessary, and can be a non-container component, where ForEach is prohibited as the root node.
@Entry
@Component
struct MyComponent {
  build() {
    // The root node must be unique and necessary, and must be a container component
    Row() {
      ChildComponent()
    }
  }
}

@Component
struct ChildComponent {
  build() {
    // The root node must be unique and necessary, and can be a non-container component
    Image('test.jpg')
  }
}
  • Local variables are not allowed to be declared, as shown in the following example.
build() {
  // Example: Local variables are not allowed
  let a: number = 1;
}
  • Direct use of console.info in UI descriptions is not allowed, but it is allowed in methods or functions, as shown in the following example.
build() {
  // Example: console.info is not allowed
  console.info('print debug log');
}
  • Calling methods that are not decorated with @Builder is not allowed; however, parameters of system components can be the return values of TS methods.
@Component
struct ParentComponent {
  doSomeCalculations() {
  }

  calcTextValue(): string {
    return 'Hello World';
  }

  @Builder doSomeRender() {
    Text(`Hello World`)
  }

  build() {
    Column() {
      // Example: Cannot call methods that are not decorated with @Builder
      this.doSomeCalculations();
      // Correct example: Can call
      this.doSomeRender();
      // Correct example: Parameters can be the return value of calling TS methods
      Text(this.calcTextValue())
    }
  }
}
  • The switch syntax is not allowed; if conditional judgment is needed, please use if. An example is shown below.
build() {
  Column() {
    // Example: switch syntax is not allowed
    switch (expression) {
      case 1:
        Text('...')
        break;
      case 2:
        Image('...')
        break;
      default:
        Text('...')
        break;
    }
    // Correct example: use if
    if(expression == 1) {
      Text('...')
    } else if(expression == 2) {
      Image('...')
    } else {
      Text('...')
    }
  }
}

  • Directly changing state variables is not allowed, as shown in the following example. For detailed analysis, see @State common issues: Direct modification of state variables in build is not allowed.
@Component
struct CompA {
  @State col1: Color = Color.Yellow;
  @State col2: Color = Color.Green;
  @State count: number = 1;
  build() {
    Column() {
      // Should avoid directly changing the value of count within the Text component
      Text(`${this.count++}`)
        .width(50)
        .height(50)
        .fontColor(this.col1)
        .onClick(() => {
          this.col2 = Color.Red;
        })
      Button("change col1").onClick(() =>{
        this.col1 = Color.Pink;
      })
    }
    .backgroundColor(this.col2)
  }
}

In ArkUI state management, state drives UI updates.

Therefore, direct changes to state variables in the build() or @Builder methods of custom components are not allowed, as this may cause the risk of circular rendering. Text('${this.count++}') will have different impacts during full updates or minimal updates:

  • Full updates (API version 8 and earlier): ArkUI may fall into an infinite re-rendering loop, as each rendering of the Text component changes the application state, triggering the next round of rendering. When this.col2 changes, the entire build function will execute, causing the text bound to Text(${this.count++}) to change, and each re-rendering of Text(${this.count++}) will update the this.count state variable, leading to a new round of build execution, thus falling into an infinite loop.

  • Minimal updates (API version 9 to present): When this.col2 changes, only the Column component will update, and the Text component will not change. Only when this.col1 changes will the entire Text component update, executing all its property functions, so you will see Text(${this.count++}) increment. Since the UI updates on a component basis, if a property of a component changes, the entire component will update. Therefore, the overall update chain is: this.col1 = Color.Pink -> Text component updates -> this.count++ -> Text component updates. It is worth noting that this writing style will cause the Text component to render twice during the initial rendering, thus impacting performance.

Changing application state in the build function can be more subtle than the examples above, such as:

  • Changing state variables within @Builder, @Extend, or @Styles methods.
  • Calling functions that change application state variables when calculating parameters, for example, Text('${this.calcLabel()}').
  • Modifying the current array, where sort() changes the array this.arr, and subsequent filter methods will return a new array.

Common Styles for Custom Components#

Custom components set common styles through chained calls with “.”.

@Component
struct MyComponent2 {
  build() {
    Button(`Hello World`)
  }
}

@Entry
@Component
struct MyComponent {
  build() {
    Row() {
      MyComponent2()
        .width(200)
        .height(300)
        .backgroundColor(Color.Red)
    }
  }
}

::: tip Note
When setting styles for custom components in ArkUI, it is equivalent to wrapping MyComponent2 in an invisible container component, and these styles are applied to the container component rather than directly to the Button component of MyComponent2. From the rendering results, we can clearly see that the red background color does not directly take effect on the Button, but rather on the invisible container component surrounding the Button.
:::

This article is synchronized and updated to xLog by Mix Space Original link: http://www.sroxck.top/posts/harmony/arkts-components

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.