HarmonyOS鸿蒙Next ArkTS中使用@Builder封装组件后,内部状态无法响应外部@State变更,如何修复?
给你一个确实可行的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
参考官网文档:如何实现组件数据双向同步。
两种方法
- 从Componentv2 的装饰器可以
- 将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构建逻辑
}
}
注意事项
- 性能考虑:
@Monitor装饰器的回调函数会在状态变化时立即执行,应避免在回调函数中执行耗时操作,以免影响UI渲染性能。 - 避免无限循环:在回调函数中修改被监听的状态变量可能导致无限循环,应谨慎处理。
- 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
不会吧。这个应该是可以的呢
在HarmonyOS Next的ArkUI中,@Builder函数默认不追踪其参数的变化。当父组件的@State items更新时,@Builder函数并不会自动重新执行,因此其内部UI不会刷新。
要解决这个问题,你需要使用**@BuilderParam装饰器配合响应式闭包**的方式来封装组件。具体方法如下:
-
将
@Builder函数转换为返回void的箭头函数,并作为参数传递: 在父组件中,定义一个@BuilderParam属性来接收这个构建函数。 -
在父组件中,使用响应式状态触发
@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的状态变更。

