HarmonyOS鸿蒙Next中如何解决List组件异步加载数据时导致的显示错误问题
HarmonyOS鸿蒙Next中如何解决List组件异步加载数据时导致的显示错误问题
【问题现象】
使用异步方法加载JSON数据并且渲染List组件时,item的数量显示错误,总数计算结果比预期值大。使用同步方法加载数据则显示正常,如下为对应的代码和显示页面。
异步加载
加载JSON文件用异步方法getRawFileContent()
,数据计算错误:
import { resourceManager } from '@kit.LocalizationKit'
import { util } from '@kit.ArkTS'
import { SayingBean } from './SayingBean'
export class SayingDataProvider {
private static sSayingList: SayingBean[]
/**
* 获取所有json歇后语实体类列表
*
* @param rm
* @returns
*/
static async getDatas(rm: resourceManager.ResourceManager) {
if (SayingDataProvider.sSayingList) {
return SayingDataProvider.sSayingList
}
let data = await rm.getRawFileContent('saying.json'); // 这里用异步方法getRawFileContent()
let text = new util.TextDecoder().decodeToString(data)
SayingDataProvider.sSayingList = JSON.parse(text)
return SayingDataProvider.sSayingList
}
}
同步加载
用同步方法getRawFileContentSync()
,数据计算正确:
let data = await rm.getRawFileContentSync('saying.json')
【背景知识】
- 解析大量JSON数据文件推荐异步操作:
getRawFileContent()
是异步方法,在读取文件内容时不会阻塞主线程。但是如果在主线程中同步执行,会阻塞UI渲染,导致应用卡顿。用异步方法可以将这些耗时操作放在后台线程执行,保证主线程的流畅性,提升用户体验。当异步操作完成时,会触发回调函数更新数据。 - List组件渲染机制: List组件在渲染时会遍历数据源,为每个数据项创建一个List item。如果数据源在渲染过程中发生变化,可能会导致渲染结果不一致。
- 生命周期函数: aboutToAppear()函数在组件即将显示时调用,用于初始化数据。在异步操作未完成时,
aboutToAppear()
可能会多次调用,导致重复添加数据。
【定位思路】
1. 数据加载时机
图中子组件SayingItem
的数据依赖于异步方法加载的数据,数据量多,耗时较久,而子组件的渲染速度一定快于数据加载速度,所以在List组件渲染时数据可能还未加载完成,导致数据显示错误。
2. 数据处理逻辑
检查数据处理逻辑,确认是否在子组件中正确处理数据。
【解决方案】
根据思路1和思路2,解决方案是将数据加载提前到父组件Index
中,确保数据加载完成后再渲染子组件,分为以下四步:
步骤1:首先在父组件中新增allList
state,用于存储异步加载的所有数据。
@Observed @State allList: SayingBean[] = [] // 新增:存储所有数据的state
步骤2:在父组件的aboutToAppear()
中加载数据。将数据加载逻辑移到父组件,并在加载完成后更新allList
state。
修改之前的代码,加载数据是在子组件SayingItem
里面,需要去掉:
@Component
struct SayingItem {
bean?: TmpSayingBean
@State totalList: SayingBean[] = []
rm: resourceManager.ResourceManager = getContext(this).resourceManager
async aboutToAppear(): Promise<void> {
// 子组件加载数据不正确,要放在父组件中
this.totalList = await SayingDataManager.sInstance.forTypeGetSayingData(this.rm, this.bean?.type as string)
}
}
修改后的代码:
@Entry
@Component
struct Index {
@State tmpSayingList: TmpSayingBean[] = []
@State allList: SayingBean[] = []
aboutToAppear(): void {
this.tmpSayingList.push(new TmpSayingBean($r('app.media.aajql'), '节气类', '节气'))
this.tmpSayingList.push(new TmpSayingBean($r('app.media.aajijielei'), '季节类', '季节'))
this.tmpSayingList.push(new TmpSayingBean($r('app.media.aarwl'), '人物类', '人物'))
this.tmpSayingList.push(new TmpSayingBean($r('app.media.aakcl'), '昆虫类', '昆虫'))
// 加载数据
SayingDataManager.sInstance.getAllDatas(getContext(this).resourceManager).then((itemlist:SayingBean[])=>{
this.allList.push(...itemlist)
})
}
}
步骤3:优化build()
方法。使用条件语句if (this.allList.length > 0)
确保数据加载完成后再渲染List组件,避免空数据渲染导致的错误。使用@Link
传递数据: 将allList
通过@Link
传递给SayingItem
组件。
build() {
Column() {
// 使用条件渲染,确保allList加载完成后再渲染List
if (this.allList.length > 0) {
List() {
ForEach(this.tmpSayingList, (bean: TmpSayingBean) => {
ListItem() {
// 通过@Link传递allList给子组件
SayingItem({ allList: $allList, bean: bean })
.margin({ bottom: 20 })
}
})
// ... (之前的代码)
}
}
}
// ... (之前的代码)
}
步骤4:在子组件SayingItem
中使用filter
过滤数据。直接使用父组件传递的allList
数据,通过filter
方法过滤出所需的数据。
@Component
struct SayingItem {
// ... (之前的代码)
@Link allList: SayingBean[] // 使用 @Link 接收父组件的数据
async aboutToAppear(): Promise<void> {
// 使用 allList 过滤数据
this.totalList = this.allList.filter(item => item.sayingKind === this.bean?.type);
}
// ... (其余代码不变)
}
【总结】
- 加载大量数据时,要进行预加载: 将数据的加载操作提前到父组件中进行,确保在List组件渲染之前数据已经加载完成。
- 在父组件中预加载之后,通过
@Link
将数据传递给子组件: 子组件不再进行数据加载,而是直接使用父组件传递的数据进行渲染。 - 动态加载数据时使用
@Observed
: 使用@Observed
装饰解析后的数据allList
,确保数据变化时能够及时触发UI更新。
更多关于HarmonyOS鸿蒙Next中如何解决List组件异步加载数据时导致的显示错误问题的实战教程也可以访问 https://www.itying.com/category-93-b0.html
1 回复
更多关于HarmonyOS鸿蒙Next中如何解决List组件异步加载数据时导致的显示错误问题的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
在HarmonyOS鸿蒙Next中,解决List组件异步加载数据时导致的显示错误问题
可以通过以下步骤实现:
- 在父组件中新增
@Observed @State allList
,用于存储异步加载的所有数据。 - 在父组件的
aboutToAppear()
中加载数据,并在加载完成后更新allList
state。 - 优化
build()
方法,使用条件语句if (this.allList.length > 0)
确保数据加载完成后再渲染List组件,并通过@Link
将allList
传递给子组件。 - 在子组件
SayingItem
中使用filter
过滤数据,直接使用父组件传递的allList
数据。
通过以上步骤,确保数据在渲染前已加载完成,避免显示错误。