HarmonyOS鸿蒙Next中ArkTS使用LazyForEach渲染长列表时,滚动到末尾后新加载的数据未触发UI更新,如何修复?
HarmonyOS鸿蒙Next中ArkTS使用LazyForEach渲染长列表时,滚动到末尾后新加载的数据未触发UI更新,如何修复?
实现分页加载列表,首次渲染 20 条数据正常。滚动到底部触发加载下一页,数据已追加到 @State items: Array<Item>,但 UI 未刷新,需手动下拉才显示,如何修复?
开发者您好,通过如下demo并未复现您的问题
示例代码如下:
export class WaterFlowDataSource implements IDataSource {
private dataArray: number[] = [];
private listeners: DataChangeListener[] = [];
constructor() {
for (let i = 0; i < 20; i++) {
this.dataArray.push(i);
}
}
// 获取索引对应的数据
public getData(index: number): number {
return this.dataArray[index];
}
// 通知控制器数据重新加载
notifyDataReload(): void {
this.listeners.forEach(listener => {
listener.onDataReloaded();
});
}
// 通知控制器数据增加
notifyDataAdd(index: number): void {
this.listeners.forEach(listener => {
listener.onDataAdd(index);
});
}
// 通知控制器数据变化
notifyDataChange(index: number): void {
this.listeners.forEach(listener => {
listener.onDataChange(index);
});
}
// 通知控制器数据删除
notifyDataDelete(index: number): void {
this.listeners.forEach(listener => {
listener.onDataDelete(index);
});
}
// 通知控制器数据位置变化
notifyDataMove(from: number, to: number): void {
this.listeners.forEach(listener => {
listener.onDataMove(from, to);
});
}
// 通知控制器数据批量修改
notifyDatasetChange(operations: DataOperation[]): void {
this.listeners.forEach(listener => {
listener.onDatasetChange(operations);
});
}
// 获取数据总数
public totalCount(): number {
return this.dataArray.length;
}
// 注册改变数据的控制器
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);
}
}
// 增加数据
public add1stItem(): void {
this.dataArray.splice(0, 0, this.dataArray.length);
this.notifyDataAdd(0);
}
// 在数据尾部增加一个元素
public addLastItem(): void {
this.dataArray.splice(this.dataArray.length, 0, this.dataArray.length);
this.notifyDataAdd(this.dataArray.length - 1);
}
// 在指定索引位置增加一个元素
public addItem(index: number): void {
this.dataArray.splice(index, 0, this.dataArray.length);
this.notifyDataAdd(index);
}
// 删除第一个元素
public delete1stItem(): void {
this.dataArray.splice(0, 1);
this.notifyDataDelete(0);
}
// 删除第二个元素
public delete2ndItem(): void {
this.dataArray.splice(1, 1);
this.notifyDataDelete(1);
}
// 删除最后一个元素
public deleteLastItem(): void {
this.dataArray.splice(-1, 1);
this.notifyDataDelete(this.dataArray.length);
}
// 在指定索引位置删除一个元素
public deleteItem(index: number): void {
this.dataArray.splice(index, 1);
this.notifyDataDelete(index);
}
// 重新加载数据
public reload(): void {
this.dataArray.splice(1, 1);
this.dataArray.splice(3, 2);
this.notifyDataReload();
}
}
import { WaterFlowDataSource } from './DataSource';
@Entry
@Component
struct WaterFlowDemo {
private minSize: number = 80;
private maxSize: number = 180;
@State colors: number[] = [0x86C5E3, 0x61CFBE, 0x8981F7, 0x86C5E3, 0x61CFBE];
scroller: Scroller = new Scroller();
dataSource: WaterFlowDataSource = new WaterFlowDataSource();
private itemWidthArray: number[] = [];
// 设置FlowItem的宽/高数组
setItemSizeArray() {
for (let i = 0; i < 20; i++) {
if (i === 0) {
this.itemWidthArray.push(this.minSize);
}
this.itemWidthArray.push(this.maxSize);
}
}
aboutToAppear() {
this.setItemSizeArray();
}
build() {
Column({ space: 2 }) {
WaterFlow() {
LazyForEach(this.dataSource, (item: number) => {
FlowItem() {
Column() {
Text(`N${item}`).fontSize(12).height('16')
}
}
.width(this.itemWidthArray[item % 100])
.height('30%')
.backgroundColor(this.colors[item % 3])
}, (item: string) => item)
}
.layoutDirection(FlexDirection.Column)
.columnsTemplate('1fr 1fr 1fr')
.rowsTemplate('1fr 1fr')
.columnsGap(10)
.rowsGap(5)
.backgroundColor(0xFAEEE0)
.width('100%')
.height('100%')
// 触底加载数据
.onReachEnd(() => {
console.info('onReachEnd');
for (let i = 0; i < 20; i++) {
this.dataSource.addLastItem();
}
})
}
}
}
LazyForEach依赖唯一键值来标识组件。不管是原来的数据数组中key值不唯一还是增删修改数组后key值不唯一,都会导致组件渲染异常。异常的表现在item缺失,重复等。如果设置了key值还是不能解决您的问题,麻烦您提供下能复现问题的完整demo吧。
更多关于HarmonyOS鸿蒙Next中ArkTS使用LazyForEach渲染长列表时,滚动到末尾后新加载的数据未触发UI更新,如何修复?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
ForEach/LazyForEach 刷新原理:如果未提供 keyGenerator,框架会基于 item 和 index 自动生成 key。默认的键值生成函数为 (item: T, index: number) => index + '__' + JSON.stringify(item)。修改状态变量数据源时,ForEach 或 LazyForEach 会捕捉到 key 的变化,从而通过重建组件节点来刷新。
参考链接:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-rendering-control-lazyforeach
小伙伴你好,简单分析了一下问题原因可能出现在:
@State 的监听限制
[@State](/user/State) 装饰器仅能感知数组第一层引用变化(如整体数组替换)若直接修改原数组(如 push() 追加元素),数组引用未变,系统无法触发 UI 更新。
能否提供一下相关代码段或示例 Demo 吗?
在HarmonyOS鸿蒙Next中,使用LazyForEach渲染长列表时,滚动到末尾后新加载数据未触发UI更新,通常是由于数据源未正确通知UI层数据变更所致。
确保数据源类继承自DataSource,并在数据更新后调用notifyDataReload()或notifyDataChange()方法。检查LazyForEach的dataSource参数是否绑定到该可观察对象。
若使用数组作为数据源,需将其包装为@Observed类中的属性,并在更新数组后,通过重新赋值整个数组或使用this.dataArray.splice()等操作触发UI刷新。直接使用push等方法可能不会触发更新。
在HarmonyOS Next中,LazyForEach与@State结合使用时,数据源更新后UI未自动刷新的问题,通常是由于数据引用未发生改变导致ArkUI框架未感知到状态变化。
核心原因:@State装饰的数组,当通过push、splice等方法直接修改其内容时,数组的引用地址未变,LazyForEach可能无法正确触发重新渲染。
解决方案:
-
为数组分配新引用(推荐): 在追加新数据时,不要直接修改原数组,而是创建一个全新的数组。
// 错误方式:直接修改原数组 // this.items.push(...newItems); // 正确方式:创建新数组 this.items = this.items.concat(newItems); // 或使用扩展运算符 this.items = [...this.items, ...newItems]; -
使用@Link或复杂状态管理: 如果数据源来自父组件,考虑使用@Link装饰器,或在子组件中使用@ObjectLink、@Observed配合自定义类,确保嵌套属性的变化能被观察到。
-
检查LazyForEach的键值生成: 确保
keyGenerator函数返回唯一且稳定的键值。新增数据的键值不能与已有数据重复,否则可能影响渲染。LazyForEach( this.items, (item: Item) => item.id.toString(), // 确保id唯一 (item: Item) => { // 列表项组件 } ) -
强制刷新(临时方案): 在极少数情况下,可以尝试通过改变数组引用来强制刷新(但应先尝试上述方法)。
// 先解构再赋值 this.items = [...this.items];
实现示例:
[@State](/user/State) items: Array<Item> = [];
loadNextPage() {
// 模拟获取新数据
const newItems: Item[] = [...];
// 正确更新:创建新数组
this.items = this.items.concat(newItems);
}
确保在滚动到底部触发加载的函数中,以上述方式更新items数组。这样LazyForEach会检测到数据源引用变化,并正确更新UI。

