鸿蒙开发:3-ForEach循环及@Observed的使用坑点

282 阅读3分钟

ForEach循环及@Observed的使用

仅讲解常见的业务开发需求使用到的地方,不详细处可查看官方文档

1、原始类型的数组

  1. ForEach循环中的item项不能直接修改,需要改数据只能修改@State修饰的数组。
@Entry
@Component
struct Index {
  @State list:string[] = ['小明','小花','大黄']

  build() {
    Column(){
      ForEach(this.list,(item:string,index:number)=>{
        Row(){
          Text(item)
            .fontSize(30)
          Button('修改').onClick(()=>{
            //这样子修改是不对的
            // item = `${item}--修改了`
            //而应该这样修改
            this.list[index] = `${item}--修改了`
          })
            .margin({left:10})
        }
        .margin({bottom:15})
      },(index:number):string=>index.toString())
    }
    .height('100%')
    .width('100%')
  }
}

1724944533782.png

2、对象类型数组

  1. 对象数组使用ForEach循环时,父组件对象数组使用@State修饰,数组中的对象必须是使用new操作符创建,字面量定义的数据修改后页面不能更新
  2. 需使用@Observed装饰器修饰对象的类
  3. 循环的内容需提出成单独组件,使用@Copmonent修饰,里面使用@ObjectLink修饰接收的数据对象
  4. @ObjectLink修饰的数据对象不能直接修改,如要修改数据可将父组件中的对象数组传递到子组件,子组件使用@Link修饰接收数据。
  5. 在一个文件中如果类(如StudentInfo)定义了构造函数constructor,以此类为类型的字面量数组会报错(如list数组),可以定义interface,让类实现此接口。
  6. 子组件中@ObjectLink修饰的对象必须是@Observed修饰的类的类型。

注意:

  1. @Observed配合@ObjectLink解决的只是对象数组数据修改后页面能监听到并自动更新的问题。
  2. 至于在子组件修改父组件数据,可以通过父组件传给子组件使用@Link双向绑定,也可以通过emitter在父组件aboutToAppear中注册监听事件,然后在子组件触发(注意需要在aboutToDisAppear中销毁)。

补充

其实对于对象数组,虽然@State只能监听到数组的第一层,我们也可以使用splice通过重置引用的方式来使页面更新,但对于频繁的更新且有图片的数据,会造成图像闪烁的问题(也很好理解,就是频繁的删除和重新插入对象),对一般低频场景使用也完全足够。

interface IStudentInfo {
  name: string
  age: number
  gradle: string
  yuwen: number
  shuxue: number
  sure: boolean
}

@Observed
class StudentInfo implements IStudentInfo {
  name: string = ''
  age: number = 0
  gradle: string = '一年级'
  yuwen: number = 0
  shuxue: number = 0
  sure: boolean = false

  constructor(instance: IStudentInfo) {
    this.name = instance.name
    this.age = instance.age
    this.gradle = instance.gradle
    this.yuwen = instance.yuwen
    this.shuxue = instance.shuxue
    this.sure = instance.sure
  }
}

const list: IStudentInfo[] = [
  {
    name: '小明',
    age: 12,
    gradle: '三年级',
    yuwen: 80,
    shuxue: 76,
    sure: false
  },
  {
    name: '小花',
    age: 12,
    gradle: '三年级',
    yuwen: 80,
    shuxue: 76,
    sure: false
  },
  {
    name: '大黄',
    age: 18,
    gradle: '三年级',
    yuwen: 80,
    shuxue: 76,
    sure: false
  },
]

@Entry
@Component
struct Index {
  @State studentlist: IStudentInfo[] = []

  aboutToAppear(): void {
    // 初始化数据
    list.forEach((value: IStudentInfo) => {
      this.studentlist.push(new StudentInfo(value))
    })
    //也可以使用.emitter.on()来注册监听事件来修改studentlist数据
  }

  aboutToDisappear(): void {
    //emitter.off()
  }

  build() {
    Column() {
      ForEach(this.studentlist, (item: StudentInfo, index: number) => {
        StudentCardItem({ item: item, index: index, list: this.studentlist })
      }, (index: number): string => index.toString())
    }
    .height('100%')
    .width('100%')
    .backgroundColor('#efefef')
  }
}


@Component
struct StudentCardItem {
  @ObjectLink item: StudentInfo
  @Link list: IStudentInfo[]
  @Prop index: number = 0

  build() {
    Column() {
      Text(this.item.name)
        .fontSize(24)
      Row() {
        Text(`语文:${this.item.yuwen}`)
          .fontSize(20)
        Text(`数学:${this.item.shuxue}`)
          .fontSize(20)
        Text(`确认成绩:${this.item.sure ? '是' : '否'}`)
          .fontSize(20)
      }
      .width('100%')
      .justifyContent(FlexAlign.SpaceBetween)

      Button(`${this.item.sure ? '取消' : '确认'}`)
        .onClick(() => {
          //这样子修改是不对的
          // item.sure = !item.sure
          //这样子修改
          this.list[this.index].sure = !this.list[this.index].sure
          //也可以使用emitter.emit来触发事件修改父组件数据
        })
        .width(60)
        .height(30)
        .fontSize(14)
        .margin({ top: 10 })
    }
    .width('100%')
    .alignItems(HorizontalAlign.Start)
    .padding({
      left: 15,
      right: 15,
      top: 15,
      bottom: 15
    })
    .backgroundColor('#fff')
    .margin({ bottom: 20 })
  }
}

1724947862351.png

如果对你有帮助,点个赞呗!!!