鴻蒙應(yīng)用示例:狀態(tài)管理與UI刷新機(jī)制從@State到@ObservedV2的進(jìn)階

b3.gif

在構(gòu)建HarmonyOS應(yīng)用時,狀態(tài)管理是一項至關(guān)重要的任務(wù)。良好的狀態(tài)管理不僅能讓應(yīng)用更加健壯,還能極大地提升用戶體驗。本文將探討三種不同層次的狀態(tài)管理策略,并分析它們對UI刷新機(jī)制的影響。

第一段代碼:基礎(chǔ)狀態(tài)管理

export class ChildBean {
  name: string = "" //item名稱
  isSelect: boolean = false //是否選中

  constructor(name: string) {
    this.name = name
  }
}

export class FatherBean {
  name: string = "" //組名稱
  childArr: ChildBean[] = []
}

@Entry
@Component
struct Index {
  @State fatherArr: FatherBean[] = []

  aboutToAppear(): void {
    //向數(shù)組添加元素
    let mFatherBean1: FatherBean = new FatherBean()
    mFatherBean1.name = '男生'
    mFatherBean1.childArr.push(new ChildBean('杰克'))
    mFatherBean1.childArr.push(new ChildBean('李雷'))
    mFatherBean1.childArr.push(new ChildBean('小草'))

    let mFatherBean2: FatherBean = new FatherBean()
    mFatherBean2.name = '女生'
    mFatherBean2.childArr.push(new ChildBean('露絲'))
    mFatherBean2.childArr.push(new ChildBean('韓梅梅'))
    mFatherBean2.childArr.push(new ChildBean('小花'))

    this.fatherArr.push(mFatherBean1)
    this.fatherArr.push(mFatherBean2)
  }

  build() {
    Column({ space: 10 }) {
      Text('請選擇班級大掃除名單')
      ForEach(this.fatherArr, (item: FatherBean, index: number) => {
        Text(`${item.name}組`)
        ForEach(item.childArr, (item_2: ChildBean, index_2: number) => {
          Row() {
            Button(`${item_2.name}`).onClick(() => {
              item_2.isSelect = !item_2.isSelect //修改數(shù)據(jù)
              //替換指定索引元素,讓數(shù)組元素地址變更,從而觸發(fā)重繪
              this.fatherArr.splice(index, 1, this.fatherArr[index]);
              //或者序列化后再反序列化。
              //this.fatherArr[index]= JSON.parse(JSON.stringify(item))
            })
            Text(`${item_2.isSelect ? '參加' : '不參加'}`)
          }
        })
      })
    }
    .width('100%')
  }
}

在第一段代碼中,我們看到使用了@State修飾符來管理狀態(tài)。這種方式適合處理較為簡單的情況,如單個變量或者簡單的數(shù)組結(jié)構(gòu)。但是,當(dāng)涉及到復(fù)雜的嵌套數(shù)據(jù)結(jié)構(gòu)時,@State的局限性就顯現(xiàn)出來了。

問題描述:

當(dāng)修改嵌套數(shù)組中的某個元素時,由于@State僅能檢測到頂層對象的變化,底層數(shù)據(jù)的變化不會自動觸發(fā)UI的更新。因此,在這段代碼中,雖然isSelect字段被修改了,但是如果沒有采取額外措施(如替換數(shù)組元素),UI不會自動刷新。

解決方案:

通過替換數(shù)組中的元素,強(qiáng)制讓數(shù)組的引用地址發(fā)生變化,從而觸發(fā)UI的重新渲染。這雖然是一個可行的解決方案,但增加了代碼的復(fù)雜性,并不是最佳實踐。

第二段代碼:@Observed與@ObjectLink的結(jié)合

@Observed
export class ChildBean {
  name: string = "" //item名稱
  isSelect: boolean = false //是否選中

  constructor(name: string) {
    this.name = name
  }
}

@Observed
export class FatherBean {
  name: string = "" //組名稱
  childArr: ChildBean[] = []
}

@Entry
@Component
struct Index {
  @State fatherArr: FatherBean[] = []

  aboutToAppear(): void {
    //向數(shù)組添加元素
    let mFatherBean1: FatherBean = new FatherBean()
    mFatherBean1.name = '男生'
    mFatherBean1.childArr.push(new ChildBean('杰克'))
    mFatherBean1.childArr.push(new ChildBean('李雷'))
    mFatherBean1.childArr.push(new ChildBean('小草'))

    let mFatherBean2: FatherBean = new FatherBean()
    mFatherBean2.name = '女生'
    mFatherBean2.childArr.push(new ChildBean('露絲'))
    mFatherBean2.childArr.push(new ChildBean('韓梅梅'))
    mFatherBean2.childArr.push(new ChildBean('小花'))

    this.fatherArr.push(mFatherBean1)
    this.fatherArr.push(mFatherBean2)
  }

  build() {
    Column({ space: 10 }) {
      Text('請選擇班級大掃除名單')
      ForEach(this.fatherArr, (item: FatherBean, index: number) => {
        Text(`${item.name}組`)
        ForEach(item.childArr, (item_2: ChildBean, index_2: number) => {
          Item({ item_2: item_2 })
        })
      })
    }
    .width('100%')
  }
}

@Component
struct Item {
  @ObjectLink item_2: ChildBean

  build() {
    Row() {
      Button(`${this.item_2.name}`).onClick(() => {
        this.item_2.isSelect = !this.item_2.isSelect //修改數(shù)據(jù)
      })
      Text(`${this.item_2.isSelect ? '參加' : '不參加'}`)
    }
  }
}

在第二段代碼中,我們看到使用了@Observed修飾符來標(biāo)記類,并通過@ObjectLink在自定義組件中引用這些對象。這種方法允許我們更細(xì)粒度地控制UI的更新邏輯。

問題描述:

雖然使用@Observed可以標(biāo)記整個類,使得其內(nèi)部的狀態(tài)變化能夠被追蹤,但對于深層嵌套的屬性,依然存在一定的局限性。此外,使用@ObjectLink需要額外定義組件,增加了代碼的復(fù)雜性。

解決方案:

通過定義自定義組件并在其中使用@ObjectLink,可以更好地管理復(fù)雜的狀態(tài)。這種方式雖然增加了編寫代碼的工作量,但同時也提供了更高的靈活性和更好的狀態(tài)隔離性。

第三段代碼:@ObservedV2與@Trace的深度觀測

@ObservedV2
export class ChildBean {
  name: string = "" //item名稱
  @Trace isSelect: boolean = false //是否選中

  constructor(name: string) {
    this.name = name
  }
}

export class FatherBean {
  name: string = "" //組名稱
  childArr: ChildBean[] = []
}

@Entry
@Component
struct Index {
  @State fatherArr: FatherBean[] = []

  aboutToAppear(): void {
    //向數(shù)組添加元素
    let mFatherBean1: FatherBean = new FatherBean()
    mFatherBean1.name = '男生'
    mFatherBean1.childArr.push(new ChildBean('杰克'))
    mFatherBean1.childArr.push(new ChildBean('李雷'))
    mFatherBean1.childArr.push(new ChildBean('小草'))

    let mFatherBean2: FatherBean = new FatherBean()
    mFatherBean2.name = '女生'
    mFatherBean2.childArr.push(new ChildBean('露絲'))
    mFatherBean2.childArr.push(new ChildBean('韓梅梅'))
    mFatherBean2.childArr.push(new ChildBean('小花'))

    this.fatherArr.push(mFatherBean1)
    this.fatherArr.push(mFatherBean2)
  }

  build() {
    Column({ space: 10 }) {
      Text('請選擇班級大掃除名單')
      ForEach(this.fatherArr, (item: FatherBean, index: number) => {
        Text(`${item.name}組`)
        ForEach(item.childArr, (item_2: ChildBean, index_2: number) => {
          Row() {
            Button(`${item_2.name}`).onClick(() => {
              item_2.isSelect = !item_2.isSelect //修改數(shù)據(jù)
            })
            Text(`${item_2.isSelect ? '參加' : '不參加'}`)
          }
        })
      })
    }
    .width('100%')
  }
}

在第三段代碼中,我們看到了使用@ObservedV2和@Trace裝飾器的組合來實現(xiàn)深度觀測。這種方法允許開發(fā)者對復(fù)雜對象的內(nèi)部狀態(tài)進(jìn)行細(xì)致的控制,并確保任何細(xì)微的變化都能被捕捉到。

問題描述:

對于復(fù)雜的嵌套對象,僅僅依靠@State或簡單的@Observed是不夠的。我們需要一種機(jī)制來確保即使是最深層的屬性變化也能被UI正確響應(yīng)。

解決方案:

使用@ObservedV2來標(biāo)記整個類,并結(jié)合@Trace來標(biāo)記需要被追蹤的具體屬性。這種方法可以實現(xiàn)對對象內(nèi)部狀態(tài)的深度追蹤,確保任何屬性的變化都能夠觸發(fā)UI的重新渲染。

總結(jié)與建議

通過對比三種不同的狀態(tài)管理策略,我們可以得出以下結(jié)論:

  1. 基本狀態(tài)管理(使用@State):適用于簡單的狀態(tài),但對于復(fù)雜數(shù)據(jù)結(jié)構(gòu)的支持不足。
  2. 中等狀態(tài)管理(使用@Observed與@ObjectLink):適合處理較為復(fù)雜的狀態(tài),但增加了代碼的復(fù)雜性。
  3. 高級狀態(tài)管理(使用@ObservedV2與@Trace):最適合處理復(fù)雜的嵌套數(shù)據(jù)結(jié)構(gòu),能夠提供深度觀測能力,確保UI準(zhǔn)確響應(yīng)狀態(tài)變化。

在實際應(yīng)用開發(fā)中,開發(fā)者應(yīng)當(dāng)根據(jù)自己的具體需求來選擇最合適的狀態(tài)管理方案。對于較為簡單的應(yīng)用,使用@State可能已經(jīng)足夠;而對于復(fù)雜應(yīng)用,尤其是涉及到多層次嵌套的狀態(tài)管理,則推薦使用@ObservedV2和@Trace的組合。

通過合理的狀態(tài)管理,我們不僅能簡化代碼結(jié)構(gòu),還能顯著提升用戶體驗,讓我們的HarmonyOS應(yīng)用更加穩(wěn)定可靠。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容