HarmonyOS Next中使用LazyForEach渲染列表,通过@ObjectLink属性变化方式刷新但是UI未更新是怎么回事?


class MyDataSource extends BasicDataSource {
  private dataArray: StringData[] = [];

  public totalCount(): number {
    return this.dataArray.length;
  }

  public getData(index: number): StringData {
    return this.dataArray[index];
  }

  public addData(index: number, data: StringData): void {
    this.dataArray.splice(index, 0, data);
    this.notifyDataAdd(index);
  }

  public pushData(data: StringData): void {
    this.dataArray.push(data);
    this.notifyDataAdd(this.dataArray.length - 1);
  }
}

@Observed
class StringData {
  message: NestedString;
  constructor(message: NestedString) {
    this.message = message;
  }  
}

@Observed
class NestedString {
  message: string;
  constructor(message: string) {
    this.message = message;
  }  
}

@Entry
@Component
struct MyComponent {
  private moved: number[] = [];
  private data: MyDataSource = new MyDataSource();

  aboutToAppear() {
    for (let i = 0; i <= 20; i++) {
      this.data.pushData(new StringData(new NestedString(`Hello ${i}`)));
    }
  }

  build() {
    List({ space: 3 }) {
      LazyForEach(this.data, (item: StringData, index: number) => {
        ListItem() {
          ChildComponent({data: item})
        }
        .onClick(() => {
          item.message.message += '0';
        })
      }, (item: StringData, index: number) => JSON.stringify(item) + index.toString())
    }.cachedCount(5)
  }
}

@Component
struct ChildComponent {
  @ObjectLink data: StringData
  build() {
    Row() {
      Text(this.data.message.message).fontSize(50)
        .onAppear(() => {
          console.info("appear:" + this.data.message.message)
        })
    }.margin({ left: 10, right: 10 })
  }
}

本文参与了 【 HarmonyOS NEXT 技术问答冲榜,等你来战!】欢迎正在阅读的你也加入。

阅读 147
avatarAI BotBETA

在 HarmonyOS Next 中使用 LazyForEach 渲染列表时,如果通过 @ObjectLink 属性变化方式刷新但 UI 未更新,这通常是由于数据变更通知机制未正确触发或组件未正确监听数据变化导致的。

在你的示例中,问题在于 StringDataNestedString 类虽然被 @Observed 修饰,但在修改 NestedStringmessage 属性时,并没有触发数据变更通知。这是因为 @Observed 只会自动监测直接在其修饰的类上的属性变化,而不会递归监测嵌套对象的属性变化。

为了解决这个问题,你可以采取以下几种方法之一:

  1. 手动触发数据变更通知
    在修改 NestedStringmessage 属性后,手动通知 StringData 对象发生变化,从而触发 UI 更新。这通常涉及到在 StringData 类中添加一个方法来通知变更。

    @Observed
    class StringData {
      message: NestedString;
      constructor(message: NestedString) {
        this.message = message;
      }
    
      updateMessage(newMessage: string) {
        this.message.message = newMessage;
        // 手动触发变更通知,例如通过设置一个标记位或使用一个观察者模式
        // 这里简单起见,可以假设有一个机制能通知到上层组件
      }
    }

然后在 MyComponent 的点击事件中调用 updateMessage 方法:

.onClick(() => {
  item.updateMessage(item.message.message + '0');
})

注意:这里的实现需要进一步完善,以确保能正确通知到 UI 组件进行刷新。

  1. 使用可观察的数据结构
    使用 HarmonyOS 提供的可观察数据结构(如 ObservableArray),这些数据结构在内部已经实现了变更通知机制。
  2. 重构数据模型
    如果可能,重构数据模型以避免嵌套的可观察对象。将需要观察的数据扁平化,直接放在最外层的可观察对象中。
  3. 使用更细粒度的组件和状态管理
    考虑将状态管理逻辑从组件中分离出来,使用全局状态管理(如 Redux、MobX 等模式的实现)来更精确地控制数据的流动和变更通知。

在你的具体情况下,由于 NestedStringmessage 属性被修改时没有触发任何变更通知,因此 UI 组件 ChildComponent 无法感知到这一变化,也就不会进行更新。你需要确保在数据变更时能够正确触发通知机制。

1 个回答

@ObjectLink装饰的成员变量仅能监听到其子属性的变化,再深入嵌套的属性便无法观测到了,因此我们只能改变它的子属性去通知对应组件重新渲染,具体参考:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides...
优化代码如下:


class MyDataSource extends BasicDataSource {
  private dataArray: StringData[] = [];

  public totalCount(): number {
    return this.dataArray.length;
  }

  public getData(index: number): StringData {
    return this.dataArray[index];
  }

  public addData(index: number, data: StringData): void {
    this.dataArray.splice(index, 0, data);
    this.notifyDataAdd(index);
  }

  public pushData(data: StringData): void {
    this.dataArray.push(data);
    this.notifyDataAdd(this.dataArray.length - 1);
  }
}

@Observed
class StringData {
  message: NestedString;
  constructor(message: NestedString) {
    this.message = message;
  }  
}

@Observed
class NestedString {
  message: string;
  constructor(message: string) {
    this.message = message;
  }  
}

@Entry
@Component
struct MyComponent {
  private moved: number[] = [];
  private data: MyDataSource = new MyDataSource();

  aboutToAppear() {
    for (let i = 0; i <= 20; i++) {
      this.data.pushData(new StringData(new NestedString(`Hello ${i}`)));
    }
  }

  build() {
    List({ space: 3 }) {
      LazyForEach(this.data, (item: StringData, index: number) => {
        ListItem() {
          ChildComponent({data: item})
        }
        .onClick(() => {
          // @ObjectLink装饰的成员变量仅能监听到其子属性的变化,再深入嵌套的属性便无法观测到
          item.message = new NestedString(item.message.message + '0');
        })
      }, (item: StringData, index: number) => JSON.stringify(item) + index.toString())
    }.cachedCount(5)
  }
}

@Component
struct ChildComponent {
  @ObjectLink data: StringData
  build() {
    Row() {
      Text(this.data.message.message).fontSize(50)
        .onAppear(() => {
          console.info("appear:" + this.data.message.message)
        })
    }.margin({ left: 10, right: 10 })
  }
}

本文参与了 【 HarmonyOS NEXT 技术问答冲榜,等你来战!】欢迎正在阅读的你也加入。

logo
HarmonyOS
子站问答
访问
宣传栏