目录
1、渲染结果非预期
2、重新渲染时图片闪烁
3、@ObjectLink属性变化UI未更新
上篇文章中我们介绍了LazyForEach的基本使用,展示了如何使用LazyForEach构造一个列表,并演示数据的添加、删除、修改如何与LazyForEach配合并正确的更新UI。本篇将介绍使用LazyForEach的时候会遇到的一些常见问题。
1、渲染结果非预期
代码如下:
import { SimpleStringDataSource } from './base/LazyForeach';@Entry
@Component
struct LazyForEachDemo3Page {private data: SimpleStringDataSource = new SimpleStringDataSource();aboutToAppear() {for (let i = 0; i <= 20; i++) {this.data.pushData(`Hello ${i}`)}}build() {List({ space: 3 }) {LazyForEach(this.data, (item: string, index: number) => {ListItem() {Row() {Text(item).fontSize(50).onAppear(() => {console.info("appear:" + item)})}.margin({ left: 10, right: 10 })}.onClick(() => {// 点击删除子组件this.data.deleteData(index);// 重置所有子组件的index索引// this.data.reloadData();})}, (item: string, index: number) => item + index.toString())}.cachedCount(5).width('100%').height('100%')}
}
运行结果如下:
当我们多次点击子组件时,会发现删除的并不一定是我们点击的那个子组件。原因是当我们删除了某一个子组件后,位于该子组件对应的数据项之后的各数据项,其index均应减1,但实际上后续的数据项对应的子组件仍然使用的是最初分配的index,其itemGenerator中的index并没有发生变化,所以删除结果和预期不符。如何修复呢?把注释的那行代码打开即可(在删除一个数据项后调用reloadData方法,重建后面的数据项,以达到更新index索引的目的)。
2、重新渲染时图片闪烁
如下代码只更新文本时,会引起图片的更新,看上去发生了闪烁:
import { StringData } from './base/LazyData';
import { ComplexDataSource } from './base/LazyForeach';@Entry
@Component
struct LazyRenderDemo4Page {private moved: number[] = [];private data: ComplexDataSource = new ComplexDataSource();aboutToAppear() {for (let i = 0; i <= 20; i++) {this.data.pushData(new StringData(`Hello ${i}`, $r('app.media.girl1')));}}build() {List({ space: 3 }) {LazyForEach(this.data, (item: StringData, index: number) => {ListItem() {Column() {Text(item.message).fontSize(50).onAppear(() => {console.info("appear:" + item.message)})Image(item.imgSrc).width(500).height(200)}.margin({ left: 10, right: 10 })}.onClick(() => {item.message += '00';this.data.reloadData();})}, (item: StringData, index: number) => JSON.stringify(item))}.cachedCount(5).width('100%').height('100%')}
}
运行效果如下:
更新文本时,图片闪烁
在我们点击ListItem子组件时,我们只改变了数据项的message属性,但是LazyForEach的刷新机制会导致整个ListItem被重建。由于Image组件是异步刷新,所以视觉上图片会发生闪烁。为了解决这种情况我们应该使用@ObjectLink和@Observed去单独刷新使用了item.message的Text组件(代码里还修改了键值对的生成规则)。
import { StringData } from './base/LazyData';
import { ComplexDataSource } from './base/LazyForeach';@Entry
@Component
struct LazyRenderDemo4Page {private moved: number[] = [];private data: ComplexDataSource = new ComplexDataSource();aboutToAppear() {for (let i = 0; i <= 20; i++) {this.data.pushData(new StringData(`Hello ${i}`, $r('app.media.girl1')));}}build() {List({ space: 3 }) {LazyForEach(this.data, (item: StringData, index: number) => {ListItem() {LazyDemo4ChildComponent({data: item})}.onClick(() => {item.message += '00';this.data.reloadData();})}, (item: StringData, index: number) => {//key不变,此时只更新相关item的数据return index.toString()})}.cachedCount(5).width('100%').height('100%')}
}@Component
struct LazyDemo4ChildComponent {@ObjectLink data: StringDatabuild() {Column() {Text(this.data.message).fontSize(50).onAppear(() => {console.info("appear:" + this.data.message)})Image(this.data.imgSrc).width(500).height(200)}.margin({ left: 10, right: 10 })}
}
修复后运行效果如下:
只更新文本,图片不闪烁
3、@ObjectLink属性变化UI未更新
import { NestedString, StringDataV2 } from './base/LazyData';
import { MyDataSource } from './base/LazyForeach';@Entry
@Component
struct LazyRenderDemo5Page {@State data: MyDataSource<StringDataV2> = new MyDataSource<StringDataV2>();aboutToAppear() {for (let i = 0; i <= 20; i++) {this.data.pushData(new StringDataV2(new NestedString(`Hello ${i}`)));}}build() {List({ space: 3 }) {LazyForEach(this.data, (item: StringDataV2, index: number) => {ListItem() {LazyRenderDemo5ChildComponent({data: item})}.onClick(() => {// item.message.message += '0';item.message = new NestedString(item.message.message + '0');})}, (item: StringDataV2, index: number) => item.toString() + index.toString())}.cachedCount(5).width('100%').width('100%')}
}@Component
struct LazyRenderDemo5ChildComponent {@ObjectLink data: StringDataV2build() {Row() {Text(this.data.message.message).fontSize(50).onAppear(() => {console.info("appear:" + this.data.message.message)})}.margin({ left: 10, right: 10 })}
}@Observed
export class StringDataV2 {message: NestedString;constructor(message: NestedString) {this.message = message;}
}@Observed
export class NestedString {message: string;constructor(message: string) {this.message = message;}
}
@ObjectLink装饰的成员变量仅能监听到其子属性的变化,再深入嵌套的属性便无法观测到了,因此我们只能改变它的子属性去通知对应组件重新渲染,具体请查看@ObjectLink与@Observed的详细使用方法和限制条件。
如何修复呢?既然不支持2层嵌套属性变化,那就在第一场嵌套属性变化上作文章,修复代码如下:
onClick(() => {item.message = new NestedString(item.message.message + '0');
})
比较简单,就是修改第一层嵌套属性的值(@ObjectLink能监听到该层的变化)。