鸿蒙状态管理之 @State

1,214 阅读4分钟

什么是状态管理

状态指的是应用在某一时刻的数据和信息。这些数据和信息可能包括用户输入、网络请求结果、UI组件的显示状态等。状态的变化时,我们应该触发UI的重新渲染,从而更新用户看到的界面。

当数据发生变化时,相应的页面元素根据最新的数据发生改变这一过程可以理解为是开发者对页面的状态管理。这一过程一般都是利用系统的某种机制自动实现的,不需要我们开发者去实现机制。

以下是华为官方文档对其状态管理的框架行为介绍:

  • 当状态变量被改变时,查询依赖该状态变量的组件;
  • 执行依赖该状态变量的组件的更新方法,组件更新渲染;
  • 和该状态变量不相关的组件或者UI描述不会发生重新渲染,从而实现页面渲染的按需更新。

千言万语不如一句代码实在,下面我们来看下具体的代码示例。

@State 的简单使用

代码示例如下:

@Entry
@Component
struct Index {
  @State message: string = 'Hello World';

  build() {
    Column() {
      Text(this.message).width('100%').height(20).margin({top: 20, bottom: 20}).textAlign(TextAlign.Center)
      Button("改变文本").onClick(() => {
        this.message = "鸿蒙";
      })
    }
    .height('100%')
    .width('100%')
  }
}

首先,我们声明了一个 @State 修饰的字符串类型字段:message。接着在 build() 中渲染了一个纵向排列的布局,布局中包含两个基础组件:Text 和 Button。

Text 组件用来显示 message 的内容,Button 用来模拟 message 的值改变过程。其他属性大家可以不用关注,这些属性只是为了页面相对看着顺眼点。效果图如下:

截屏2024-07-04 16.39.45.png

在 DevEco Studio 的实时预览中,点击按钮的效果如下:

录屏2024-07-04 16.40.32.gif

在预览图中可以看到:在我们点击了按钮之后,Text 的内容就换成了更新之后的字符串 - "鸿蒙"了。

在对 @State 使用方式有了大概了解之后,下面我们来详细的了解下具体的使用规则。

具体规则

声明时必须指定类型以及本地初始化

因为 ArkTS 是有类型推断的,声明普通变量时,我们可以直接通过给变量赋值来自动推断其类型,比如下面的代码:

name = "abc";

它与name: string = "abc";是等同的。

但如果用 @State 修饰变量的话,就不能这么写了,必须显式的指出其是什么类型:

// 编译报错:The 'message' attribute must have its type specified. <ArkTSCheck>
@State message = 'Hello World'; 

还有就是必须给 @State 修饰的变量赋初始值:

// 编译报错:Property 'message' has no initializer and is not definitely assigned in the constructor. <ArkTSCheck>
@State message: string;

如果修饰的变量数据可能为空的话,推荐用以下写法:

@State message: string | undefined = undefined;

不推荐像下面这么写:

@State message: string = undefined;

@State 修饰变量也可以从父组件中传递过来,这样就会覆盖掉子组件中本地初始化的值:

@Entry
@Component
struct Index {

  build() {
    Column() {
      FatherComponent();
    }
  }
}

@Component
struct FatherComponent {
  build() {
    SonComponent({message: "父组件的传值"})
  }
}

@Component
struct SonComponent {
  @State message: string = "hello, world";

  build() {
    Column() {
      Text(this.message).width('100%').height(20).margin({top: 20, bottom: 20}).textAlign(TextAlign.Center)
      Button("改变文本").onClick(() => {
        this.message = "鸿蒙";
      })
    }
    .height('100%')
    .width('100%')
  }

效果图如下:

截屏2024-07-05 09.47.15.png

可以看到子组件中的 "hello, world" 被父组件中的:"父组件的传值" 覆盖掉了。

支持的数据类型

@State 支持的类型如下:

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

需要注意的是,类嵌套类的话,被嵌套类的属性变化是检测不到的:

@Entry
@Component
struct Index {
  @State cat: Cat = new Cat(new Food("鸡肉"));

  build() {
    Column() {
      Text(this.cat.food.name).width('100%').height(20).margin({top: 20, bottom: 20}).textAlign(TextAlign.Center)
      Button("改变文本").onClick(() => {
        this.cat.food.name = "鸿蒙";
      })
    }
    .height('100%')
    .width('100%')
  }
}


class Cat {
  food: Food;

  constructor(food: Food) {
    this.food = food;
  }
}

class Food {
  name: string;

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

比如上面的代码,Cat 类中嵌套了 Food 类。如果修改 Food 的 name 属性,系统框架是监听不到这个变化的。 这种情况需要使用 @Observed@ObjectLink 来进行处理。

总结

@State 是鸿蒙状态管理中最基础的一个状态装饰器,它必须指定类型和本地初始化。支持 Object、class、string、number、boolean、enum类型,以及这些类型的数组。支持Date类型,API11及以上支持Map、Set类型。