在鸿蒙中使用 Repeat 渲染嵌套列表,修改内层列表的一个元素,页面不会更新

3 阅读1分钟

问题:在鸿蒙中使用 Repeat 渲染嵌套列表,修改内层列表的一个元素,页面不会更新

数据结构如下:

  interface IListData {
    id: number,
    name: string,
  }

  listData: [
    {
      id: 1,
      name: 'A',
      list: [
        {
          id: 1,
          name: '张三'
        },
        {
          id: 2,
          name: '李四'
        }
      ]
    }
  ]

渲染代码如下:

  List({ space: 20 }) {
    Repeat(this.listData).each((ri: RepeatItem<IReportListResponse_list>) => {
      ListItem() {
        Column() {
          Text(this.handleNursingTime(ri.item.name))
            .fontColor('#333333')
            .fontSize('16fp')
            .margin({ bottom: 10 })
          Column() {
            Repeat(ri.item.nursingModelInfoList).each((riChild: RepeatItem<INursingModelInfo>) => {
              Row() {
                Text(this.handleNursingTime(riChild.item.name))
            }).key((item: INursingModelInfo, index) => `${item.id}`)
          }
          .backgroundColor(Color.White)
          .borderRadius(12)
        }
        .alignItems(HorizontalAlign.Start)
      }
    }).key((item: IReportListResponse_list, index) => `${item.id}`)

调用方法删除内层列表的一个元素:

  deleteByReportIndex(id: number) {
    const arr = this.listData.map((item) => {
      item.list = item.list.filter((childItem) => {
        return childItem.id !== id;
      })
      return item;
    })

    this.listData = arr.filter((item) => {
      return item.list.length > 0;
    })
  }

此时内层的元素已被删除了一个,但是页面不会更新

如何解决这个问题?

在 HarmonyOS Next 的状态管理 V2 (@ObservedV2 + @Trace) 中,最核心的规则是:只有真正的类实例(Class Instance)才能被观测,普通的 JSON 对象即便赋值给被装饰的变量,也无法触发深度监听。
直接将接口返回的 JSON 对象赋值给了 this.listData,没有实例化 IListData 类。导致 @Trace 装饰器完全失效,ArkUI 把它当做普通对象处理,所以修改内层数据无法驱动 UI 刷新。

解决方案

  1. 为了让内层列表渲染更稳定,建议给内层的数据也定义一个 @ObservedV2 的类。

  interface IList {
    id: number;
    name: string;
  }

  @ObservedV2
  class IListData {
    id: number;
    name: string;
    @Trace list: IList[];
    constructor(id: number, name: string, list: IList[]) {
      this.id = id;
      this.name = name;
      this.list = IList;
    }
  }
  1. 在接口请求回来后,必须用 new 关键字转换数据。

export struct HistoryList {
  @Local listData: IListData[] = [];

  async fetchData(): Promise<void> {
    const res: AxiosResponse<IBaseResponse<ReportListResponse>> = await getReportList(1, this.pageSize);
    if (res.data.success) {
      this.data = res.data.data;
      this.listData = res.data.data.list.map((item: IReportListResponse_list) => {
        // 这里必须 new 你的类
        return new IListData(item.id, item.name, item.list);
      });
    }
  }

  build(){

  }
}
  1. 这样,当你修改 listData 中的任何一个元素时,UI 都会自动更新。