鸿蒙开发——状态管理

@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})
          }
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容