DEV Community

Cover image for Nested Components, Decorator Errors, Iterative List Refresh, Unit Conversion, Tabs Component Lifecycle
kouwei qing
kouwei qing

Posted on

Nested Components, Decorator Errors, Iterative List Refresh, Unit Conversion, Tabs Component Lifecycle

[Daily HarmonyOS Next Knowledge] Nested Components, Decorator Errors, Iterative List Refresh, Unit Conversion, Tabs Component Lifecycle

1. Issues with Nested Components in HarmonyOS ArkUI

  1. How to wrap a component?
  2. How to wrap the build() function of a @Component-decorated component to implement generic loading and error displays?

Reference demo:

import { CommComponent } from './CommComponent'
import { ComponentStatus } from './CommonEnums'

@Component
export struct Index2 {
  @State componentStatus: ComponentStatus = ComponentStatus.SUCCESS;
  build() {
    Column() {
      // Pass builder content from the caller
      CommComponent({ componentStatus: this.componentStatus, builder: this.successBuild })
    }.height('50%').width('100%')
  }

  @Builder
  successBuild() {
    Text('成功的展示')
      .fontSize(30)
      .width(100)
      .height(100)
      .borderWidth(2)
  }
}

// CommComponent's builder method (simplified)
build() {
  this.builder()
}
Enter fullscreen mode Exit fullscreen mode

2. Error with @Concurrent Decorator in HarmonyOS

The @Concurrent decorator always reports an error:

"Decorator function return type is ‘void | TypedPropertyDescriptor’ but is expected to be ‘void’ or ‘any’. Type ‘TypedPropertyDescriptor’ is not assignable to type ‘void’."

Reference code:

import taskpool from '@ohos.taskpool';

@Concurrent
function testPromise(args1: number, args2: number): Promise<number> {
  return new Promise<number>((testFuncA, testFuncB) => {
    testFuncA(args1 + args2);
  });
}

export class FCDemoTest {
  public static instance: FCDemoTest = new FCDemoTest();

  private constructor() {}

  checkInfo() {
    let task1: taskpool.Task = new taskpool.Task(testPromise, 1, 2);
    taskpool.execute(task1).then((d: object) => {
      console.info("task1 res is: " + d)
    }).catch((e: object) => {
      console.info("task1 catch e: " + e)
    })
  }
}
Enter fullscreen mode Exit fullscreen mode

3. Refresh Issues with ForEach Lists in HarmonyOS

The publish time in a list does not update after refreshing because the data source remains identical. However, the display time is calculated as a difference between the current time and the publish time (e.g., "just now", "2 minutes ago"). Since ForEach only checks if the data source changes, the display does not update.

Solutions:

  1. Add the time difference to the data source, update it during refresh, and use it as the key in keyGenerator.
  2. Use a custom key generator with a dynamic value (e.g., timestamp) to force updates.
  3. Toggle between two identical lists to trigger re-rendering.

Example code:

@Entry
@Component
struct Index {
  @State one: boolean = true;
  @State two: boolean = true;

  build() {
    Row() {
      Column() {
        Row() {
          Text('A')
            .layoutWeight(1)
          if (this.two) {
            Toggle({ type: ToggleType.Switch, isOn: this.one })
              .enabled(!this.two)
              .onChange((isOn) => {
                this.one = isOn;
              })
          } else {
            Toggle({ type: ToggleType.Switch, isOn: this.one })
              .enabled(!this.two)
              .onChange((isOn) => {
                this.one = isOn;
              })
          }
        }

        Row() {
          Text('B')
            .layoutWeight(1)
          Toggle({ type: ToggleType.Switch, isOn: this.two })
            .onChange((isOn) => {
              this.two = isOn;
            })
        }
        .margin({ top: "25vp" })
      }
      .width('100%')
      .margin({ left: '24vp', right: '24vp' })
      .layoutWeight(1)
    }
    .height('100%')
  }
}
Enter fullscreen mode Exit fullscreen mode

4. Unit Conversion for RenderNode in HarmonyOS

How to convert between units (e.g., vp and px) to align drawRect dimensions with the frame?

Unit conversion reference:

https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/ts-pixel-units-V5

ArkUI provides 4 pixel units, with vp as the base unit:

Unit Description
px Physical screen pixels
vp Screen density-dependent pixels (default unit if unspecified).
Note: The vp-to-px ratio depends on screen density.
fp Font pixels (scale with system font size)
lpx Logical viewport pixels (ratio of actual to logical screen width, default designWidth=720)

Conversion APIs:

| Method | Description |
| :-- | :-- |
| vp2px(value: number): number | Convert vp to px |
| px2vp(value: number): number | Convert px to vp |
| fp2px(value: number): number | Convert fp to px |
| px2fp(value: number): number | Convert px to fp |
| lpx2px(value: number): number | Convert lpx to px |
| px2lpx(value: number): number | Convert px to lpx |

5. How to Ensure a Tab Page Runs Code Every Time it Appears in HarmonyOS? (onPageShow Not Triggered)

The onPageShow lifecycle method does not trigger when switching tabs. Instead, use the component-level aboutToAppear() method.

Recommended approach:

  1. Use onTabBarClick(event: (index: number) => void) in the parent component to track the active tab index.
  2. Pass the active index to child components via @State and @Props.
  3. Use @Watch in child components to detect index changes and call showPage().

Example implementation:

// Parent component
Tabs() {
  TabContent() {
    ChildComponent({ activeTabIndex: $currentTabIndex })
  }
  .onTabBarClick((index) => {
    this.currentTabIndex = index;
  })
}

// Child component
@Component
struct ChildComponent {
  @Props activeTabIndex: number;

  @Watch('onTabIndexChange')
  onTabIndexChange() {
    this.showPage();
  }

  showPage() {
    // Code to run when tab appears
  }
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)