HarmonyOS鸿蒙Next ArkTS中使用@Builder封装组件后,内部状态无法响应外部@State变更,如何修复?

HarmonyOS鸿蒙Next ArkTS中使用@Builder封装组件后,内部状态无法响应外部@State变更,如何修复? 将列表项封装为 [@Builder](/user/Builder) itemBuilder(data: Item),当父组件 [@State](/user/State) items 更新时,子项 UI 未刷新,这应该如何解决

12 回复

给你一个确实可行的demo:

@Extend(Text) function textSty(size: Resource, color: Resource, lineHeight: number) {
  .fontSize(size).fontColor(color).lineHeight(lineHeight).textAlign(TextAlign.End)
}
@Entry
@Component
struct Index {
  @State items: ItemObj = { title: '哈哈哈', value: '111' }
  build() {
    Column(){
      Text('更新对象').onClick(() => {
        this.items.title = '新字段'
      })
      Text('下面显示内容:').margin({top: 20, bottom: 20})
      childView({ item: this.items })
    }.margin({top: 100})
  }
}

@Builder export function childView($$: childItem) {
  Row(){
    Text($$.item.title).textSty($r('app.float.font_size_3'), $r('app.color.adm_color_text_secondary'), 20)
    Text($$.item.value + '').textSty($r('app.float.font_size_3'), $r('app.color.adm_color_text'), 20).textOverflow({overflow:TextOverflow.None}).margin({left: 20}).layoutWeight(1)
  }.width('100%').margin({top: 6, bottom: 6}).justifyContent(FlexAlign.SpaceBetween).alignItems(VerticalAlign.Top)
}

interface childItem{
  item: ItemObj
}
export interface ItemObj{
  title?: string
  value?: string
}

更多关于HarmonyOS鸿蒙Next ArkTS中使用@Builder封装组件后,内部状态无法响应外部@State变更,如何修复?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


两种方法

  1. 从Componentv2 的装饰器可以
  2. 将Builder 装饰器的函数 改成 component 装饰 采用组件化

应该是可以的,传引用,可参考示例:

// 步骤1:定义参数接口
interface SliderParam {
  label: string;
  value: number;
}

// 步骤2:封装@Builder组件(使用对象引用)
@Builder
function ParamSlider(param: SliderParam) {
  Column() {
    Text(param.label)
      .height(30)
  }
  .width('100%')
  .height(40)
}


@Entry
@Component
struct ParentComponent {
  @State sliderValue: number = 30; // 外部状态
  @State sliderKey: string = "音量"; // 外部状态
  num: number = 1;

  build() {
    Column() {
      // 传递对象引用
      ParamSlider({
        label: this.sliderKey,
        value: this.sliderValue
      })


      Text("change Title")
        .margin({top:20})
        .width('100%')
        .height(40)
        .onClick(() => {
          this.num++;
          this.sliderKey = "Key title " + this.num;
        })
    }
  }
}

在ComponentV2装饰的自定义组件中使用Monitor

概述

在ComponentV2装饰的自定义组件中,可以使用@Monitor装饰器来监听组件内部状态的变化,并在变化时执行相应的逻辑。@Monitor装饰器可以监听@State@Prop@Link@ObjectLink@Provide@Consume@StorageProp@StorageLink@LocalStorageProp@LocalStorageLink装饰的变量。

使用场景

  • 监听状态变化:当组件内部的状态发生变化时,执行特定的逻辑。
  • 动态更新UI:在状态变化时,动态更新UI,确保UI与数据同步。
  • 避免UI不更新问题:在动态更新数据时,确保UI能够正确响应数据变化。

使用方法

1. 基本用法

在自定义组件中,使用@Monitor装饰器监听状态变量的变化。当状态变量发生变化时,装饰器中的回调函数会被调用。

import { ComponentV2, Monitor, State } from '@ohos/arkui';

@ComponentV2
struct MyComponent {
  @State count: number = 0;

  @Monitor('count')
  onCountChange(newValue: number, oldValue: number) {
    console.log(`count changed from ${oldValue} to ${newValue}`);
  }

  build() {
    // UI构建逻辑
  }
}

2. 监听多个状态

可以同时监听多个状态变量,当任何一个被监听的状态发生变化时,回调函数都会被调用。

import { ComponentV2, Monitor, State } from '@ohos/arkui';

@ComponentV2
struct MyComponent {
  @State count: number = 0;
  @State name: string = '';

  @Monitor(['count', 'name'])
  onStateChange(propertyName: string, newValue: any, oldValue: any) {
    console.log(`${propertyName} changed from ${oldValue} to ${newValue}`);
  }

  build() {
    // UI构建逻辑
  }
}

3. 动态更新UI

在回调函数中,可以执行UI更新逻辑,确保UI与数据同步。

import { ComponentV2, Monitor, State } from '@ohos/arkui';

@ComponentV2
struct MyComponent {
  @State data: string[] = [];

  @Monitor('data')
  onDataChange() {
    // 当data变化时,更新UI
    this.updateUI();
  }

  updateUI() {
    // 更新UI的逻辑
  }

  build() {
    // UI构建逻辑
  }
}

注意事项

  1. 性能考虑@Monitor装饰器的回调函数会在状态变化时立即执行,应避免在回调函数中执行耗时操作,以免影响UI渲染性能。
  2. 避免无限循环:在回调函数中修改被监听的状态变量可能导致无限循环,应谨慎处理。
  3. UI更新时机@Monitor装饰器的回调函数执行时机早于UI更新,可以在回调函数中准备UI更新所需的数据。

示例

以下是一个完整的示例,演示如何在ComponentV2装饰的自定义组件中使用@Monitor装饰器监听状态变化并更新UI。

import { ComponentV2, Monitor, State } from '@ohos/arkui';

@ComponentV2
struct MyTable {
  @State tableData: string[] = ['Item 1', 'Item 2', 'Item 3'];

  @Monitor('tableData')
  onTableDataChange() {
    console.log('Table data updated, refreshing UI...');
    // 触发UI更新
  }

  build() {
    Column() {
      ForEach(this.tableData, (item: string) => {
        Text(item)
          .fontSize(20)
          .margin(10)
      })
    }
  }
}

在这个示例中,当tableData发生变化时,onTableDataChange回调函数会被调用,并触发UI更新,确保表格数据动态更新时UI同步刷新。

总结

使用@Monitor装饰器可以方便地监听ComponentV2装饰的自定义组件中的状态变化,并在变化时执行相应的逻辑,确保UI与数据同步。在动态更新数据时,合理使用@Monitor装饰器可以避免UI不更新的问题。

@Builder纯函数式模板,不持有状态,其响应性依赖传入参数的引用变化。需确保:

  • 传入的是新对象或新数组元素,而非修改原对象属性;
  • Item 为 class,需用 @Observed 装饰,并通过 @ObjectLink 传递;

找HarmonyOS工作还需要会Flutter的哦,有需要Flutter教程的可以学学大地老师的教程,很不错,B站免费学的哦:https://www.bilibili.com/video/BV1S4411E7LY/?p=17

没碰到

有要学HarmonyOS AI的同学吗,联系我:https://www.itying.com/goods-1206.html

不会吧。这个应该是可以的呢

在ArkTS中,使用@Builder封装组件时,若内部状态无法响应外部@State变更,通常是因为@Builder默认不追踪外部状态变化。
修复方法:

  1. @Builder声明为[@Builder](/user/Builder) function MyBuilder($$) { ... },通过参数$$显式传递外部状态。
  2. @Builder内部使用$$.参数名引用外部状态,确保状态变更能触发UI更新。
    示例:
[@Builder](/user/Builder) function MyBuilder($$) {  
  Text($$.myState)  
}  

这样外部@State变更时,@Builder内部能同步响应。

在HarmonyOS Next的ArkUI中,@Builder函数默认不追踪其参数的变化。当父组件的@State items更新时,@Builder函数并不会自动重新执行,因此其内部UI不会刷新。

要解决这个问题,你需要使用**@BuilderParam装饰器配合响应式闭包**的方式来封装组件。具体方法如下:

  1. @Builder函数转换为返回void的箭头函数,并作为参数传递: 在父组件中,定义一个@BuilderParam属性来接收这个构建函数。

  2. 在父组件中,使用响应式状态触发@BuilderParam的重新调用: 将@Builder函数包装在一个响应式闭包中,当items变化时,闭包会重新执行,从而触发UI更新。

示例代码

// 子组件
@Component
struct ChildComponent {
  @BuilderParam itemBuilder: () => void

  build() {
    Column() {
      this.itemBuilder() // 执行构建函数
    }
  }
}

// 父组件
@Entry
@Component
struct ParentComponent {
  @State items: Item[] = []

  // 使用响应式闭包包装构建逻辑
  @Builder
  buildItem(data: Item) {
    // 你的列表项UI构建代码
    Text(data.name)
      .onClick(() => {
        // 处理点击事件
      })
  }

  build() {
    Column() {
      ForEach(this.items, (item: Item) => {
        ChildComponent({
          // 关键:将构建函数作为箭头函数传递,并绑定当前响应式数据
          itemBuilder: () => {
            this.buildItem(item)
          }
        })
      })
    }
  }
}

关键点

  • @BuilderParam装饰的属性可以接收一个返回void的箭头函数。
  • ForEach中,每次迭代都会创建一个新的箭头函数闭包,该闭包捕获当前的item值。
  • this.items变化时,ForEach会重新执行,生成新的闭包,从而触发子组件重新构建。

这种方法确保了@Builder封装的组件能够正确响应外部@State的状态变更。

回到顶部