HarmonyOS鸿蒙Next中新请求数据和上次数据相同时,LazyForEach内子组件不更新的问题如何解决
HarmonyOS鸿蒙Next中新请求数据和上次数据相同时,LazyForEach内子组件不更新的问题如何解决
【问题现象】
在实际的业务中,经常遇到两次请求返回数据完成相同的情况,但是要求即使数据相同,也要刷新LazyForEach相关的组件。
因为,尽管第一次和第二次请求返回的数据相同,但是通常会根据第一次返回的数据进行某些处理和相关组件的刷新;第二次请求时,需要重置组件的状态。
如果直接使用返回的相关数据作为LazyForEach的键值,不会触发相关组件的状态刷新。
【背景知识】
LazyForEach键值生成规则:
在LazyForEach循环渲染过程中,系统会为每个item生成一个唯一且持久的键值,用于标识对应的组件。当这个键值变化时,ArkUI框架将视为该数组元素已被替换或修改,并会基于新的键值创建一个新的组件。
LazyForEach提供了一个名为keyGenerator的参数,这是一个函数,开发者可以通过它自定义键值的生成规则。如果开发者没有定义keyGenerator函数,则ArkUI框架会使用默认的键值生成函数,即:
(item: any, index: number) => { return viewId + '-' + index.toString(); }
相关参考文档:
【解决方案】
LazyForEach渲染的原理:只有当keyGenerator参数定义的键值发生变化时,ArkUI框架将才会将该数组元素视为已被替换或修改,并会基于新的键值创建一个新的组件。上述问题中,两次请求返回的数据完全相同时,使用返回数据作为键值将不会触发组件刷新。而业务要求只要有新的请求数据,就必须刷新对应的UI组件,即使数据是相同的。这就要求定义一个合适的keyGenerator参数,对相同元素也能产生不同的键值。
下面的解决方案采用在键值生成中加入随机数的方法:
@Entry
@Component
export struct Test11 {
@State bol:boolean = true
@State debtData: CommonDataSource<Data> = new CommonDataSource([])
data1:Data[] = [new Data('分组1 name1','分组1 code1'),new Data('分组1 name2','分组1 code2')]
aboutToAppear(): void {
this.debtData.setNewData(this.data1)
}
build() {
Column(){
Text('重新请求数据').width('100%').height(40).fontColor(Color.Red).onClick(()=>{
this.getDetData()
})
List() {
LazyForEach(this.debtData, (data: Data, index: number) => {
ListItem() {
TestComponent({data:data})
}.height(80)
.onClick(()=>{
data.stockCode = data.stockCode + 'ss'
})
}, (value: Data) => JSON.stringify(value) + Math.random())
}
.width('100%')
.layoutWeight(1)
.listDirection(Axis.Vertical)
.divider({ strokeWidth: 5, color: 'app.color.dz_root_background' })
}
}
// 获取数据
getDetData() {
if(this.bol){
this.bol = false
this.debtData.setNewData([new Data('分组1 name1','分组1 code1'),new Data('分组1 name2','分组1 code2')])
}else {
this.bol = true
this.debtData.setNewData([new Data('分组1 name1','分组1 code1'),new Data('分组1 name2','分组1 code2')])
}
}
}
@Component
export struct TestComponent {
@ObjectLink data:Data
build() {
Text(this.data.stockCode).fontColor(Color.Black).fontSize(20)
}
}
@Observed
export class Data {
stockName: string = ''
stockCode: string = ''
constructor(name:string,code:string) {
this.stockName = name
this.stockCode = code
}
}
export class CommonDataSource<T> implements IDataSource {
private dataArray: T[] = [];
private listeners: DataChangeListener[] = [];
constructor(element: T[]) {
this.dataArray = element;
}
public getData(index: number) {
return this.dataArray[index]
}
public getDataList(){
return this.dataArray
}
public totalCount(): number {
return this.dataArray.length;
}
public getIndex(data: T): number {
return this.dataArray.indexOf(data);
}
public addArrayData(data: T[]): void {
this.dataArray = this.dataArray.concat(data);
this.notifyDataReload();
}
public setNewData(data: T[]): void {
this.dataArray = [];
this.addArrayData(data);
}
public addData(index: number, data: T[]): void {
this.dataArray = this.dataArray.concat(data);
this.notifyDataAdd(index);
}
public pushData(data: T): void {
this.dataArray.push(data);
this.notifyDataAdd(this.dataArray.length - 1);
}
public clearData(): void {
this.dataArray = [];
}
public refresh(): void {
this.notifyDataReload()
}
unregisterDataChangeListener(listener: DataChangeListener): void {
const pos = this.listeners.indexOf(listener);
if (pos >= 0) {
this.listeners.splice(pos, 1);
}
}
registerDataChangeListener(listener: DataChangeListener): void {
if (this.listeners.indexOf(listener) < 0) {
this.listeners.push(listener);
}
}
notifyDataReload(): void {
this.listeners.forEach((listener: DataChangeListener) => {
listener.onDataReloaded();
})
}
notifyDataAdd(index: number): void {
this.listeners.forEach((listener: DataChangeListener) => {
listener.onDataAdd(index);
})
}
notifyDataChange(index: number): void {
this.listeners.forEach((listener: DataChangeListener) => {
listener.onDataChange(index);
})
}
notifyDataDelete(index: number): void {
this.listeners.forEach((listener: DataChangeListener) => {
listener.onDataDelete(index);
})
}
notifyDataMove(from: number, to: number): void {
this.listeners.forEach((listener: DataChangeListener) => {
listener.onDataMove(from, to);
})
}
}
解释:
首次运行的代码的结果如下:
点击“分组1 code1”和“分子1 code2”,对应属性发生变化。
接下来,点击“重新请求数据”,返回的数据和原始数据相同,对应的“分组1 code1ss”和“分子1 code2ss”应该恢复到原始状态,如下:
更多关于HarmonyOS鸿蒙Next中新请求数据和上次数据相同时,LazyForEach内子组件不更新的问题如何解决的实战教程也可以访问 https://www.itying.com/category-93-b0.html
更多关于HarmonyOS鸿蒙Next中新请求数据和上次数据相同时,LazyForEach内子组件不更新的问题如何解决的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
在LazyForEach中,即使两次请求返回的数据相同,也可以通过自定义keyGenerator
参数来强制刷新子组件。具体方法是在键值生成中加入随机数,确保每次请求生成的键值不同。例如:
LazyForEach(this.debtData, (data: Data, index: number) => {
ListItem() {
TestComponent({data:data})
}.height(80)
.onClick(()=>{
data.stockCode = data.stockCode + 'ss'
})
}, (value: Data) => JSON.stringify(value) + Math.random())
这样,即使数据相同,生成的键值也会不同,从而触发组件的刷新。