鸿蒙状态管理之@Prop

358 阅读3分钟

前言

上一篇文章中,我们介绍了状态管理的概念和最基础的装饰器:@State。本篇文章讲解一下可以和父组件建立单向同步的状态管理装饰器:@Prop

基本使用

@Prop 用于父组件向子组件的单向状态管理。假设我们有一个子组件用来展示用户信息,其所展示的用户信息是由父组件传递进来的。而且用户信息要随着父组件的更新而更新。示例代码如下:

@Entry
@Component
struct Index {
  @State userName: string = "rose";

  build() {
    Column() {
      Button("修改用户名")
        .onClick(() => { // 此处模拟用户名修改
          this.userName = "jack";
        })
      UserInfo({ userName: this.userName })
    }
  }
}

@Component
struct UserInfo {
  @Prop userName: string = "";

  build() {
    Text(this.userName)
  }
}

父组件 IndexuserName 传递给子组件 UserInfo 进行展示。当点击了《修改用户名》的按钮之后,父组件的 userName 更新为 “jack”,此时子组件的用户名展示也会更新为 “jack”。

示例图如下:

录屏2024-07-09 14.01.07.gif

Tips:如果将子组件中的 userName 用 @State 装饰器修饰,点击了父组件中的按钮,子组件中的显示不会随之刷新。感兴趣的同学可以自己写代码验证一下。

具体规则

不强制本地初始化

对于 @Prop 装饰器修饰的变量,可以本地初始化,也可以从父组件进行初始化。需要注意的是两者至少有一项。比如下面这三种情况都是可以的:

  • 只在本地进行初始化,代码如下:
@Entry
@Component
struct Index {
  build() {
    Column() {
      UserInfo()
    }
  }
}

@Component
struct UserInfo {
  @Prop userName: string = "";

  build() {
    Text(this.userName)
  }
}
  • 只在父组件进行初始化,代码如下:
@Entry
@Component
struct Index {
  @State userName: string = "rose";

  build() {
    Column() {
      UserInfo({ userName: this.userName })
    }
  }
}

@Component
struct UserInfo {
  @Prop userName: string;

  build() {
    Text(this.userName)
  }
}
  • 即在本地进行初始化,也在父组件进行初始化(父组件的会覆盖掉本地的),代码如下:
@Entry
@Component
struct Index {
  @State userName: string = "rose";

  build() {
    Column() {
      UserInfo({ userName: this.userName })
    }
  }
}

@Component
struct UserInfo {
  @Prop userName: string = "sam";

  build() {
    Text(this.userName)
  }
}

虽然有以上三种情况,但结合 @Prop 装饰器的实际使用场景(父组件到子组件的数据单向同步),无论如何我们都是应该从父组件对其初始化的,不然我们在子组件中用 @Prop 装饰器就没有什么实际的意义了。

Tips:需要注意的是,如果你既没有在本地初始化也没有在父组件中初始化,实际上编译器也不会报错。如果就不好排查了...

必须指定类型,且类型需与同步的类型一致

首先第一条,@Prop 装饰器修饰的变量必须显式的指定类型,不能借助类型推断:

@Prop userName = "sam";

上面的代码编译器会报以下的错误:The 'userName' attribute must have its type specified. <ArkTSCheck>。 其次就是 @Prop 的类型必须与父组件传入的类型一致。比如下面的代码就是不允许的:

@Entry
@Component
struct Index {
  @State userName: number = 0

  build() {
    Column() {
      Button("修改用户名")
        .onClick(() => {
          this.userName = "jack";
        })
      UserInfo({ userName: this.userName })
    }
  }
}

@Component
struct UserInfo {
  @Prop userName: string = "sam";

  build() {
    Text(this.userName)
  }
}

编译器会提示报以下的错误:Type 'number' is not assignable to type 'string'. <ArkTSCheck>

如果传入的是数组或者类,数组的元素类型或者类的类型也要对应一致。

允许装饰的变量类型

支持的类型如下:

  • 基础类型:string、number、boolean、enum及相关类型的数组。
  • 对象类型:Object、class、Date 及相关类型的数据。
  • 集合类型:Map、Set(API 11 以上才支持)。
  • 联合类型:支持上述类型的联合类型,比如:string | number。

总结

@Porp 装饰器用于父组件与子组件的单向数据同步。它可以本地初始化也可以父组件初始化,且父组件传入的类型要与 @Porp 装饰器修饰的字段类型一致。