HarmonyOS鸿蒙Next中在使用@Observed和@ObjectLink时,替换数组里的某个值不能更新UI,是怎么回事?

HarmonyOS鸿蒙Next中在使用@Observed@ObjectLink时,替换数组里的某个值不能更新UI,是怎么回事? 在使用@Observed@ObjectLink时,我替换了数组object里的某个值,但是不能更新UI,只能完全替换整个object对象,才能更新

4 回复

【背景知识】 可以将@Observed注解加到类上,将@ObjectLink注解加到组件的属性上,这样类和组件就构成了一个整体,当类的属性值发生变化,组件会同步跟随刷新;以此为基础重复操作,这样就实现了当复杂嵌套类(如二维数组、三维数组、对象map集合等)属性变化时,UI跟随着刷新。

【解决方案】 在主界面上使用嵌套类数组,使用Button组件修改嵌套类的属性值,页面跟随着刷新变化,效果如下:

具体实现步骤如下:

  1. 创建一个最基础的类和对应的UI组件。
// 第一层UI组件
@Component
struct FirstItemComponent {
  @ObjectLink firstItem: FirstItem
  private TestF: Function = (v: string) => {}

  build() {
    Column() {
      Column() {
        Text(`id:${this.firstItem.id}, text: ${this.firstItem.text}`).fontSize(20).textAlign(TextAlign.Center)
        Button('子组件调用父组件方法修改')
          .onClick(() => {
            this.TestF('测试')
          })
      }.width('100%').justifyContent(FlexAlign.Center)
    }.width('100%').justifyContent(FlexAlign.Center).backgroundColor('#ffd9cba0')
  }
}

// 第一层最基础的信息类
@Observed
class FirstItem {
  id?: number
  text?: string

  constructor(id: number, text: string) {
    this.id = id;
    this.text = text;
  }
}
  1. 创建第二层嵌套类和对应的UI组件。
@Component
struct SecondComponent {
  @ObjectLink secondItem: SecondItem
  private TestS: Function = (id: number, v: string) => {}

  build() {
    Column() {
      Row() {
        Text(`id:${this.secondItem.id}, text: ${this.secondItem.text}`).fontSize(20).textAlign(TextAlign.Center)
      }.width('100%').justifyContent(FlexAlign.SpaceBetween)

      Row() {
        List() {
          ForEach(this.secondItem.itemList, (item: FirstItem, index: number) => {
            ListItem() {
              Column() {
                FirstItemComponent({
                  firstItem: item, TestF: (v: string) => {
                    this.TestS(index, v)
                  }
                })
              }
            }
          })
        }
      }.width('100%')
    }
    .padding(10)
    .backgroundColor('#ffa3cba3')
    .width('100%')
  }
}

@Observed
class SecondItem {
  id?: number
  text?: string
  // 第一层基础信息的数组
  itemList?: Array<FirstItem>

  constructor(id: number, text: string, itemList: Array<FirstItem>) {
    this.id = id;
    this.text = text;
    this.itemList = itemList;
  }
}
  1. 绘制主界面UI。
@Entry
@Component
struct IndexPage {
  // 第二层数组
  @State itemInfos: SecondItem[] = [
    new SecondItem(1, `secondItem1`, [new FirstItem(10, `firstItem1`), new FirstItem(11, `firstItem2`)]),
    new SecondItem(2, `secondItem2`, [new FirstItem(13, `firstItem3`), new FirstItem(14, `firstItem4`)])
  ]

  testF(index2: number, v: string, index: number) {
    this.itemInfos[index].itemList![index2].text = v
    console.log('V==> ' + v + 'index2==> ' + index2 + 'index==> ' + index)
  }

  build() {
    Column() {
      Row() {
        List({ space: 2 }) {
          ForEach(this.itemInfos, (item: SecondItem, index: number) => {
            ListItem() {
              SecondComponent({
                secondItem: item, TestS: (index2: number, v: string) => {
                  this.testF(index2, v, index)
                }
              })
            }
          })
        }
      }.width('100%').justifyContent(FlexAlign.SpaceBetween)

      Column() {
        Button('父组件,改变数组[0][1]的text值,看是否会变化')
          .onClick(() => {
          this.itemInfos[0].itemList![1].text = new Date().toString()
        })

        Divider().height(10)
        Button('父组件,改变数组[1][0]的text值,看是否会变化')
          .onClick(() => {
          this.itemInfos[1].itemList![0].text = new Date().toString()
        })
      }.margin({ top: 50 }).width('100%').justifyContent(FlexAlign.SpaceBetween)

    }.width('100%').justifyContent(FlexAlign.Center)
  }
}

【常见FAQ】 Q:嵌套过深是否会影响性能? A:建议不超过三层嵌套。

更多关于HarmonyOS鸿蒙Next中在使用@Observed和@ObjectLink时,替换数组里的某个值不能更新UI,是怎么回事?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


应该是您在赋值的时候没有使用全量替换,而是直接修改了数组中的某个元素,可以尝试替换整个数组。例如,如果你有一个数组items,而不是items[index] = newItem,你可以使用this.items = […items, newItem]或者this.items = items.map((item, idx) => idx === index ? newItem : item)。这样做的原因是赋值操作会引发数组地址的变化,从而触发UI的更新。 您可以参考如下demo:

@Observed
export class UIModel {
  id?: string
  title?: string
  content?: string
  img?: Resource
  type?: number
  color?: ResourceColor
  option?: ResourceColor | boolean
  defaultImg?: Resource
  activeImg?: Resource

  constructor(type: number, titel: string) {
    this.title = titel
    this.type = type
  }
}

@Component
@Entry
struct Index {
  @State btnInfos: UIModel[] = [
    new UIModel(0, '天重复'), new UIModel(1, '周重复'), new UIModel(2, '月重复'), new UIModel(3, '年重复')
  ]

  build() {
    Column() {
      ForEach(this.btnInfos, (value: UIModel) => {
        ObjectLinkChild({ test: value })
      })
    }
  }
}

@Component
struct ObjectLinkChild {
  @ObjectLink test: UIModel;

  build() {
    Text(`ObjectLinkChild testNum ${this.test.type}`)
      .onClick(() => {
        // 可以对ObjectLink装饰对象的属性赋值
        if (this.test.type != undefined) {
          this.test.type = this.test.type + 1;
        }
      })
  }
}

在HarmonyOS鸿蒙Next中,@Observed@ObjectLink用于数据绑定和UI更新。如果替换数组中的某个值后UI未更新,可能是因为直接替换数组元素未触发数据变化通知。@Observed@ObjectLink依赖于数据变化通知机制来更新UI。建议使用数组的set方法或重新赋值整个数组,以确保数据变化被正确捕获并触发UI更新。

在HarmonyOS Next中,@Observed@ObjectLink的数组更新问题通常是由于数据响应式机制导致的。当直接修改数组元素时,框架无法检测到这种变化。这是因为:

  1. @Observed@ObjectLink主要监控对象引用变化,而不是深层次属性变化
  2. 直接修改数组元素(index赋值)不会触发响应式更新

解决方案:

  1. 使用数组的拷贝替换方式:
// 错误方式 - 不会触发更新
this.array[index] = newValue;

// 正确方式 - 创建新数组
this.array = [...this.array.slice(0, index), newValue, ...this.array.slice(index+1)];
  1. 对于复杂对象数组,建议:
// 先拷贝整个数组
const newArray = [...this.array];
// 修改指定元素
newArray[index] = {...newArray[index], ...updatedProperties};
// 最后整体替换
this.array = newArray;

这是因为HarmonyOS的响应式系统需要明确的对象引用变更才能触发UI更新。当处理数组时,应该总是返回一个新的数组引用而不是直接修改现有数组。

回到顶部