鸿蒙开发——状态管理

@State

状态变量

1、在当前页面使用
2、@State修饰的成员变量改变,会让UI跟着一起刷新更改。
3、@State修饰的如果是class或Object时,只能监听到第一层属性的更改。

示例1

用@State修饰一个string类型的成员变量,点击按钮时修改它的值,发现UI也发生变化

@State message: string = "hello"

Column() {
    Text(this.message)

    Button("按钮").width(100).height(40).onClick(() => {
       this.message = "你好"
    })
}

示例2

用@State修饰一个Object类型的成员变量
1.点击按钮1时修改它的整体值,发现UI也发生变化
2.点击按钮2时修改它的第一层值,发现UI发生变化
3.点击按钮3时修改它的第二层值,发现UI未发生变化,其实值是有改变的
4.点击按钮4时修改它的第一层对象整个值,发现UI发生变化

// 声明对象类型
interface Animal {
  color: string
  weight: number
}

interface Man {
  name: string
  age: number
  dog: Animal
}

// @State定义成员变量
@State man: Man = {
    name: "jonas",
    age: 31,
    dog: {
      color: "white",
      weight: 30
    }
  }


// 界面使用
Text(`man is ${this.man.name}, his dog color is ${this.man.dog.color}`)
        Button("按钮1").width(100).height(40).onClick(() => {
          this.man = {
            name: "BOb",
            age: 31,
            dog: {
              color: "black",
              weight: 30
            }
          }
        })

        Button("按钮2").width(100).height(40).onClick(() => {
          this.man.name = "Joe"
        })

        Button("按钮3").width(100).height(40).onClick(() => {
          this.man.dog.color = "red"
        })

        Button("按钮4").width(100).height(40).onClick(() => {
          this.man.dog = {
            color : "yellow",
            weight: 30
          }
        })

@Prop 单向传递

父子组件单向传递

1、@Prop装饰的变量可以和父组件建立单向的同步关系
2、@Prop装饰的变量是可以变的,但是变化不会同步回其父组件

示例1

下面例子中,父组件声明了一个状态变量car 子组件声明了一个单向传递变量car来接收父组件的car。
默认展示的就是父子组件开的车都是宝马,当点击父组件的换车按钮,发现父子组件都变成了开奔驰。如果是点击子组件换车按钮,发现只有子组件变为了开奔驰,父组件没变。

@Component
struct FatherComponent {

  @State car: string = "宝马"

  build() {
    Column() {
      Text("父组件")
      Text(`我开着${this.car}`)
      Button("换车").onClick(()=>{
        this.car = "奔驰"
      })

      SonComponent({car: this.car})

    }
  }
}

@Component
struct SonComponent {
  @Prop car: string

  build() {
    Column() {
      Text("子组件")
      Text(`开着爸爸的${this.car}`)
      Button("换车").onClick(()=>{
        this.car = "奔驰"
      })
    }
  }
}

示例2

如果希望子组件的状态修改后能够影响父组件的状态,就需要使用回调函数来间接修改
下面例子中子组件增加了一个函数类型changeCar属性,在父组件中传入一个函数,回调里中返回一个string值,将这个值赋值给自己的car状态属性。当点击子组件的换车按钮时,调用changeCar并传入新的值"奔驰",此时父组件的car值变为"奔驰",连带着子组件也会跟着变化。

@Component
struct FatherComponent {

  @State car: string = "宝马"

  build() {
    Column() {
      Text("父组件")
      Text(`我开着${this.car}`)
      Button("换车").onClick(()=>{
        this.car = "奔驰"
      })
      // 这里必须用箭头函数,否则this.car找到的就是子组件里的car了
      SonComponent({car: this.car, changeCar: (car: string) => {
        this.car = car;
      }})

    }
  }
}

@Component
struct SonComponent {
  @Prop car: string
  changeCar = (car:string) => {}

  build() {
    Column() {
      Text("子组件")
      Text(`开着爸爸的${this.car}`)
      Button("换车").onClick(()=>{
        this.changeCar("奔驰")
      })
    }
  }
}

@Link 双向同步

可以实现父组件和子组件的双向同步

1、父组件定义一个状态属性
2、子组件用@Link修饰同名属性
3、父组件将状态属性传递给子组件

示例

还是@Prop的例子,我们将@Prop改为@Link就可以实现修改子组件的属性也让父组件的属性跟着变化了

@Component
struct FatherComponent {

  @State car: string = "宝马"

  build() {
    Column() {
      Text("父组件")
      Text(`我开着${this.car}`)
      Button("换车").onClick(()=>{
        this.car = "奔驰"
      })

      SonComponent({car: this.car})

    }
  }
}

@Component
struct SonComponent {
  @Link car: string 

  build() {
    Column() {
      Text("子组件")
      Text(`开着爸爸的${this.car}`)
      Button("换车").onClick(()=>{
        this.car = "奔驰"
      })
    }
  }
}

@Provide + @Consume 跨层级双向同步

创建三层UI,第一层为爷爷层,第二层父亲层,第三层儿子层,三层嵌套,爷爷层在最底部。

爷爷层如果想让自己的状态属性跨越父亲层与孙子层状态属性同步,按照之前的几种方法必须要一层层先传递给父亲层,再让父亲层传递给孙子层,这样是非常繁琐的。

这时候使用@Provide + @Consume就可以跨多层级直接同步

爷爷层用@Provide声明一个状态属性 ,孙子层用@Consume声明一个同名的状态属性来接收,就可以直接同步了。

示例

下面的例子点击爷爷层的换车按钮,爷爷层和孙子层的UI都会更改
点击孙子层的换车按钮,爷爷层和孙子层的UI也都会更改

@Component
struct GrandFatherComponent {
  @Provide car: string = "宝马"

  build() {
    Column() {
      Text("爷爷组件")
      Text(`我开着${this.car}`)
      Button("换车").onClick(()=>{
        this.car = "奔驰"
      })
      FatherComponent()
    }
  }
}

@Component
struct FatherComponent {
  build() {
    Column() {
      Text("父组件")
      SonComponent()
    }
  }
}

@Component
struct SonComponent {
  @Consume car: string
  build() {
    Column() {
      Text("子组件")
      Text(`开着爷爷的${this.car}`)
      Button("换车").onClick(()=>{
        this.car = "奔驰"
      })
    }
  }
}

@Observed + @ObjectLink 监听深层属性双向同步

说明:上面学的@Link修饰器修饰的状态属性只能监听到第一层的值变动,如果想监听更深层次的状态属性就要使用@Observed + @ObjectLink
作用:用于在涉及嵌套对象或数组的场景中进行双向数据同步
注意:@ObjectLink修饰符不能用在Entry修饰的组件中,@Observed 只能修饰class不能修饰interface

示例1

下面例子@Observed修饰Man类,循环创建多个自定义组件,并将数组中的对象传入。在自定义组件中用@ ObjectLink接收,在自定义组件内改变对象值,父组件中会被同步更改并刷新UI。

@Observed
class Man {
  name: string
  age: number

  constructor(name: string, age: number) {
     this.name = name;
     this.age = age;
  }
}

@Component
struct Cell {
  @ObjectLink man: Man

  build() {
    Column() {
      Text(`我的名字叫${this.man.name}`)
      Button("按钮").onClick(()=>{
          this.man.name = "bob"
      })
    }
  }
}



@Component
export struct HomePage {
@State list: Man[] = [new Man("jonas", 31), new Man("mary", 29)]

    build() {
          Column() {
              List({space: 10}) {
                   ForEach(this.list, (item: Man, index: number) => {
                      Cell({man: item})
                  })
              }
          }
    }
}

示例2

定义Man对象它有个对象属性Son, Son类用@ Observed修饰。在父组件中,将man对象的son属性传递给子组件,子组件用@ObjectLink接收。子组件中修改son的name属性,可以同步修改父组件的man属性的son值,不过并不能触发父组件UI刷新。
如果子组件用@Link接收会闪退,用@Prop接收,并不同步修改父组件的值。

@Observed
class Son {
  name: string

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

class Man {
  name: string
  son: Son

  constructor(name: string, son: Son) {
     this.name = name;
     this.son = son;
  }
}

@Component
struct Item {
  @ObjectLink son: Son

  build() {
    Column() {
      Text(`子视图中儿子的名字是${this.son.name}`)
      Button("按钮").onClick(()=>{
        this.son.name = "BOB"
      })
    }
  }
}


@Component
export struct HomePage {
@State list: Man[] = [new Man("jonas", 31), new Man("mary", 29)]

    build() {
          Column() {
                Text(`父视图中我的儿子的名字是${this.man.son.name}`)
                Item({son: this.man.son})
          }
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349

推荐阅读更多精彩内容