HarmonyOS 鸿蒙Next中懒加载首次异步获取数据不更新

HarmonyOS 鸿蒙Next中懒加载首次异步获取数据不更新

//懒加载数据源//不能用@state只能用private
private mDataSource: MDataSource = new mDataSource()
async aboutToAppear() {
  let list = await this.getAllData()
  this.mDataSource.updateAllDate(list)
}
public updateAllDate(dataList: AAA[]) {
  this.originDataArray = new ArrayList<AAA>();
  dataList.forEach((item) => {
    try {
      this.originDataArray.add(item);
    } catch (e) {
    }
  });
  this.notifyDataReload();
}

AAA:

export class AAA {

  name?: string

  date?: string

  age?: string

  text?: string
}

为什么打开页面后异步获取到数据后页面没有展示


更多关于HarmonyOS 鸿蒙Next中懒加载首次异步获取数据不更新的实战教程也可以访问 https://www.itying.com/category-93-b0.html

5 回复

【问题分析】

1.notifyDataReload 实现不完整:你调用了 notifyDataReload(),但如果你的 MDataSource 类没有正确地管理 DataChangeListener 列表,并遍历调用它们的 onDataReloaded() 方法,UI 是收不到通知的。

2.totalCount 和 getData 没有返回新数据:在 notifyDataReload 发出通知时,LazyForEach 会立即回调 totalCount()。如果此时 totalCount 还是 0,UI 就不会更新。

【解决方案】

需要一个基类(通常叫 BasicDataSource)来专门处理监听器的注册和分发。请检查你的代码是否包含了这部分逻辑。

【代码参考示例】

// 1. 定义数据模型
export class AAA {
  name?: string = ""
  date?: string = ""
  age?: string = ""
  text?: string = ""

  constructor(name: string) {
    this.name = name;
  }
}

// 2. 核心:实现 IDataSource 接口的基类
// 这一步非常关键,LazyForEach 靠这里的监听器列表来感知变化
class BasicDataSource implements IDataSource {
  private listeners: DataChangeListener[] = [];

  // 获取数据总数,由子类实现
  public totalCount(): number {
    return 0;
  }

  // 获取具体数据,由子类实现
  public getData(index: number): AAA {
    return new AAA("default");
  }

  // 【关键】注册监听器:系统会在 build 的时候自动调用这个方法注册 UI 监听
  registerDataChangeListener(listener: DataChangeListener): void {
    if (this.listeners.indexOf(listener) < 0) {
      this.listeners.push(listener);
    }
  }

  // 【关键】注销监听器
  unregisterDataChangeListener(listener: DataChangeListener): void {
    const pos = this.listeners.indexOf(listener);
    if (pos >= 0) {
      this.listeners.splice(pos, 1);
    }
  }

  // 【关键】通知 UI 重新加载所有数据
  notifyDataReload(): void {
    this.listeners.forEach(listener => {
      listener.onDataReloaded();
    })
  }

  // 通知 UI 添加了数据
  notifyDataAdd(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataAdd(index);
    })
  }

  // 其他通知方法(Change, Move, Delete)可按需实现...
}

// 3. 具体的业务数据源
class MDataSource extends BasicDataSource {
  // 这里演示使用原生数组,使用 ArrayList 逻辑也是一样的
  private dataArray: AAA[] = [];

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

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

  // 更新所有数据的方法
  public updateAllDate(dataList: AAA[]) {
    // 1. 替换数据
    this.dataArray = dataList;
    // 2. 通知 UI 刷新
    // 注意:这里调用的是父类的 notifyDataReload,它会遍历 listeners 通知 UI
    this.notifyDataReload();
  }
}

@Entry
@Component
struct LazyLoadPage {
  // 数据源不需要 @State,因为 LazyForEach 监听的是内部事件,不是引用变化
  private mDataSource: MDataSource = new MDataSource();

  async aboutToAppear() {
    // 模拟异步请求
    console.info("LazyLoad: 开始异步请求");
    let list = await this.mockApiGetData();
    console.info("LazyLoad: 数据获取成功,准备刷新 UI");

    // 更新数据源
    this.mDataSource.updateAllDate(list);
  }

  // 模拟一个异步 API 请求
  mockApiGetData(): Promise<AAA[]> {
    return new Promise((resolve) => {
      setTimeout(() => {
        let result: AAA[] = [];
        for (let i = 0; i < 20; i++) {
          result.push(new AAA(`User ${i}`));
        }
        resolve(result);
      }, 2000); // 模拟 2秒 延迟
    })
  }

  build() {
    Column() {
      Text("懒加载异步刷新示例")
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .margin(10)

      List({ space: 10 }) {
        // 使用 LazyForEach
        LazyForEach(this.mDataSource, (item: AAA, index: number) => {
          ListItem() {
            Row() {
              Text(`索引: ${index}`).fontSize(14).fontColor(Color.Gray)
              Text(item.name).fontSize(16).margin({ left: 10 })
            }
            .width('100%')
            .padding(20)
            .backgroundColor('#F5F5F5')
            .borderRadius(8)
          }
        }, (item: AAA) => JSON.stringify(item)) // Key生成规则
      }
      .width('100%')
      .layoutWeight(1) // 占满剩余空间
    }
    .width('100%')
    .height('100%')
  }
}

更多关于HarmonyOS 鸿蒙Next中懒加载首次异步获取数据不更新的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


没用,我用的是官网的StringData类型数组的BasicDataSource代码,

问题已解决,感谢大佬,

在HarmonyOS Next中,懒加载首次异步获取数据不更新,通常是因为数据状态未正确触发UI刷新。请检查以下方面:

  1. 确保使用@State@Link装饰器标记数据变量,以便数据变化时自动更新UI。
  2. 在异步回调中更新数据时,需在async函数内使用await,或通过Task处理,确保数据变更在UI线程执行。
  3. 懒加载组件如LazyForEach需配合ArkUI的数据管理机制,确保数据源更新后通知组件重新渲染。

若问题仍存在,检查异步操作是否成功返回数据,并确认数据绑定逻辑正确。

问题出在数据源的声明和更新机制上。在提供的代码中,mDataSource 被声明为 private 而非 @State 装饰的响应式变量。在 aboutToAppear 生命周期中,虽然异步获取了数据并调用了 updateAllDate 方法,但 mDataSource 本身不是响应式的,因此其内部 originDataArray 的变化不会触发UI重新渲染。

解决方案:

  1. 确保数据源是响应式的:如果 MDataSource 类内部管理的数据需要驱动UI更新,该类本身或其关键属性应使用 @State 装饰。由于你提到“不能用@state只能用private”,这通常意味着 MDataSource 可能是一个 LazyForEach 的数据源,其更新应通过其自身的通知机制(如 notifyDataReload)触发。但关键点是,持有该数据源的组件属性必须是响应式的,ArkUI框架才能感知到数据源的变更。

  2. 修正组件代码:在页面组件中,应将 mDataSource 声明为 @State@Link(如果从父组件传入),以确保当数据源内部调用 notifyDataReload() 时,框架能关联到该数据源并更新UI。

    @State private mDataSource: MDataSource = new MDataSource(); // 使用 @State 装饰
    
    async aboutToAppear() {
      let list = await this.getAllData();
      this.mDataSource.updateAllDate(list); // 此时 updateAllDate 内部的 notifyDataReload 将能触发UI更新
    }
    
  3. 检查数据源实现:确保 MDataSource 正确实现了 IDataSource 接口,并且 updateAllDate 方法中的 notifyDataReload() 调用是有效的。从你提供的 updateAllDate 代码看,逻辑是合理的:清空原数组、添加新数据、通知重载。

总结:根本原因是 mDataSource 未被声明为响应式变量(如 @State),导致其内部的状态变化无法被ArkUI框架追踪,进而UI不会更新。将其改为 @State private mDataSource: MDataSource 即可解决。

回到顶部