在HarmonyOS NEXT开发中通过WrappedBuilder.builder创建的Component不能更新Component,是否WrappedBuilder.builder不支持更新?

在HarmonyOS NEXT开发中通过WrappedBuilder.builder创建的Component不能更新Component,是否WrappedBuilder.builder不支持更新?通过WrappedBuilder.builder在创建一个component,能执行首次builder,然后更新父组件state,不能再次触发builder,通过builder创建的组件不更新。

build() { 
  Column() { 
    if (this.dxItemRenderService && this.section && this.templateItem) { 
 
      Text(this.section?.template?.name) 
      this.dxItemRenderService?.getDxWrappedBuilder().builder(this.section) 
      Text(this.section?.template?.name) 
    } else { 
      Text('not found' + this.templateItem?.name); 
    } 
  } 
  .width('100%') 
  .opacity(this.searchBarHasRender()) 
  .onAreaChange(this.searchBarAreaChange.bind(this)) 
}
阅读 1.3k
avatarAI BotBETA

在 HarmonyOS NEXT 的 ArkUI 框架中,WrappedBuilder.builder 的更新机制需要注意以下几点:

核心原因
WrappedBuilder.builder 的更新需要依赖 响应式状态变化。如果父组件没有正确触发状态更新,或 WrappedBuilder 未正确绑定响应式依赖,会导致无法触发二次构建。

解决方案

  1. 强制刷新父组件状态
    确保父组件中 this.section@State/@Prop 等响应式装饰器修饰的变量。修改时需 改变对象引用 而非仅修改属性:

    @State section: Section = ...;
    
    // 错误方式(不会触发更新):
    this.section.template.name = "newName"; 
    
    // 正确方式(创建新对象):
    this.section = { ...this.section, template: { ...this.section.template, name: "newName" } };
  2. Builder 参数绑定
    检查 dxItemRenderService.getDxWrappedBuilder() 的实现,确保其内部绑定的是响应式变量。建议在自定义 Builder 时使用 @BuilderParam 并显式声明参数依赖:

    // WrappedBuilder 定义方
    @Builder
    function DxWrappedBuilder(section: Section) { ... }
    
    // 使用方
    this.dxItemRenderService?.getDxWrappedBuilder()(this.section)
  3. Key 强制刷新
    为动态构建的组件添加唯一性标识,当 section 变化时通过 key 强制销毁重建:

    Column() {
      this.dxItemRenderService?.getDxWrappedBuilder()
     .builder(this.section)
     .key(this.section.template.id) // 唯一标识
    }
  4. 检查构建函数纯净性
    确保 getDxWrappedBuilder().builder()无副作用 的纯函数,不依赖外部缓存状态。

代码修改建议

build() {
  Column() {
    if (this.dxItemRenderService && this.section && this.templateItem) {
      Text(this.section.template?.name)
      this.dxItemRenderService.getDxWrappedBuilder()
        .builder(this.section)
        .key(this.section.template.id) // 添加 key 强制更新
      Text(this.section.template?.name)
    } else {
      Text('not found' + this.templateItem?.name)
    }
  }
  .width('100%')
  .opacity(this.searchBarHasRender())
}

原理说明
ArkUI 框架通过比较组件树的节点标识(包括类型、key、属性等)来决定是否重新渲染。WrappedBuilder 生成的组件如果没有正确绑定依赖或标识,会导致框架误判为无需更新。通过显式声明响应式依赖和添加唯一 key 可强制触发更新。

2 个回答

WrappedBuilder.builder是支持更新Component的,但需要正确的方法来实现。在使用WrappedBuilder.builder时,要确保能够更新Component,关键在于如何传递参数和管理状态。

首先,需要理解WrappedBuilder的设计目的。它是用于封装全局@Builder的一个工具,允许开发者通过引用传递参数,从而实现UI的动态更新。这意味着,当你使用WrappedBuilder.builder时,可以通过传递引用类型的数据来触发UI的更新。

参考以下示例,其中使用WrappedBuilder.builder来创建一个可以更新的Component:

class Tmp {

paramA2: string = 'hello';

}

@Builder
function overBuilder(param: Tmp) {

Column(){
    Text(`value: $${param.paramA2}`)
}

}

const wBuilder: WrappedBuilder<[Tmp]> = wrapBuilder(overBuilder);

@Entry
@Component
struct Parent{

@State label: Tmp = new Tmp();

build(){
    Column(){
        wBuilder.builder({paramA2: this.label.paramA2})
        Button('Click me').onClick(() => {
            this.label.paramA2 = 'ArkUI';
        })
    }
}

}
在这个示例中,overBuilder通过引用传递接受一个Tmp类型的参数。这个参数在一个Parent组件中被使用,并且可以通过修改label.paramA2的值来触发UI的更新。当点击按钮时,paramA2的值会被更新,从而导致UI也得到更新。

注意事项

参数传递 :确保传递到Builder的参数是引用类型,这样它们的变更可以被检测到,并触发UI更新。
状态管理 :使用State或其他状态管理技术来管理那些可能变更的值,确保这些值的变更能够反映到UI上。