@State装饰嵌套结构的变量时,为什么嵌套结构中数组元素增加或减少时界面不会刷新?

问题现象

如下代码点击增加元素或减少元素,增加或减少嵌套在对象中的数组元素,界面不会有变化。

界面代码如下:

@Entry 
@Component 
struct Index { 
  @State dataList :TestDataList = new TestDataList([]); 
 
  aboutToAppear() { 
    this.dataList.datas = [new TestData('0',0),new TestData('1',1)]; 
  } 
 
  build() { 
    Column(){ 
      ForEach(this.dataList.datas,(data:TestData)=>{ 
        Text(data.getName()); 
      }) 
      Button('增加元素').onClick((e)=>{ 
        this.dataList.datas.push(new TestData(this.dataList.datas.length+'',this.dataList.datas.length)); 
      }).width('100%') 
      Button('减少元素').onClick((e)=>{ 
        this.dataList.datas.pop(); 
      }).width('100%') 
 
    }.height('100%') 
    .alignItems(HorizontalAlign.Center) 
    .justifyContent(FlexAlign.Start) 
  } 
}

数据类如下:

@Observed 
export class TestData { 
  public value : number; 
  public name : string; 
 
  constructor(name: string, value: number) { 
    this.name = name; 
    this.value = value; 
  } 
  getValue():number{ 
    return this.value + 100; 
  } 
 
  getName(): string { 
    return `name is: ${this.name} value is ${this.getValue()}`; 
  } 
} 
 
export class TestDataList { 
  public datas: Array<Object> 
 
  constructor(datas: Array<Object>) { 
    this.datas = datas; 
  } 
}
阅读 578
1 个回答

可能原因

以上代码引起不刷新的问题主要是由于嵌套结构导致的:TestDataList这个类中嵌套存放了一个数组,数组中存放了一个类结构TestData。

//以上数据结构为 
--TestDataList 
 └─Array 
    └─TestData 
      │——name 
      └─value

由于@State装饰的变量,只能监听到对象本身的地址以及第一层属性的地址变化,也就是

this.dataList地址的变化或者this.dataList.datas的地址变化,例如如下操作:
this.dataList = new TestDataList(...) 
or 
this.dataList.datas = new Array(...)

由于this.dataList.datas.push、pop的操作不会触发dataList以及datas的地址变化,也就不会触发UI的刷新。

解决措施

解决方案是利用ArkUI提供的@Observed、@ObjectLink来对嵌套的结构建立UI与数据的联系,对于数组结构需要通过@Observed包装一层,使其成为可监听结构;

具体方案如下:

  1. 将Array变为ObseredArray,使用Observed监听。

    //xxx.ets 
    @Observed 
    export class ObservedArray<T> extends Array<T> { 
      constructor(args?: T[]) { 
     if (args instanceof Array) { 
       super(...args); 
     } else { 
       super(); 
     } 
      } 
    }
  2. 替换原来的Array为ObseredArray。

    export class TestDataList { 
      datas: ObservedArray<TestData> = new ObservedArray(); 
    }
  3. 视图中列表部分创建一个新的组件ListDataView,用ObjectLink接受数据。

    @Component 
    struct ListDataView{ 
      @ObjectLink datas:ObservedArray<TestData> 
      build() { 
     Column() { 
       ForEach(this.datas,(data:TestData)=>{ 
         Text(data.getName()) 
       }) 
     } 
      } 
    }
  4. 原来的Index引入ListDataView使用,并传递参数。

    import { ObservedArray, TestData, TestDataList } from './TestData'; 
     
    @Entry 
    @Component 
    struct Index { 
      @State dataList :TestDataList = new TestDataList(); 
     
      aboutToAppear() { 
     this.dataList.datas.push(...[new TestData('0',0),new TestData('1',1)]); 
      } 
     
      build() { 
     Column(){ 
       ListDataView({datas:this.dataList.datas}) 
       Button('增加元素').onClick((e)=>{ 
         this.dataList.datas.push(new TestData(this.dataList.datas.length+'',this.dataList.datas.length)); 
       }).width('100%') 
       Button('减少元素').onClick((e)=>{ 
         this.dataList.datas.pop(); 
       }).width('100%') 
     }.height('100%') 
     .alignItems(HorizontalAlign.Center) 
     .justifyContent(FlexAlign.Start) 
      } 
    }
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进