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
【问题分析】
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刷新。请检查以下方面:
- 确保使用
@State或@Link装饰器标记数据变量,以便数据变化时自动更新UI。 - 在异步回调中更新数据时,需在
async函数内使用await,或通过Task处理,确保数据变更在UI线程执行。 - 懒加载组件如
LazyForEach需配合ArkUI的数据管理机制,确保数据源更新后通知组件重新渲染。
若问题仍存在,检查异步操作是否成功返回数据,并确认数据绑定逻辑正确。
问题出在数据源的声明和更新机制上。在提供的代码中,mDataSource 被声明为 private 而非 @State 装饰的响应式变量。在 aboutToAppear 生命周期中,虽然异步获取了数据并调用了 updateAllDate 方法,但 mDataSource 本身不是响应式的,因此其内部 originDataArray 的变化不会触发UI重新渲染。
解决方案:
-
确保数据源是响应式的:如果
MDataSource类内部管理的数据需要驱动UI更新,该类本身或其关键属性应使用@State装饰。由于你提到“不能用@state只能用private”,这通常意味着MDataSource可能是一个LazyForEach的数据源,其更新应通过其自身的通知机制(如notifyDataReload)触发。但关键点是,持有该数据源的组件属性必须是响应式的,ArkUI框架才能感知到数据源的变更。 -
修正组件代码:在页面组件中,应将
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更新 } -
检查数据源实现:确保
MDataSource正确实现了IDataSource接口,并且updateAllDate方法中的notifyDataReload()调用是有效的。从你提供的updateAllDate代码看,逻辑是合理的:清空原数组、添加新数据、通知重载。
总结:根本原因是 mDataSource 未被声明为响应式变量(如 @State),导致其内部的状态变化无法被ArkUI框架追踪,进而UI不会更新。将其改为 @State private mDataSource: MDataSource 即可解决。

