首页 > 编程笔记 > 通用技能 阅读:4

ArkTS @State用法详解(附带实例)

在 ArkTS 中,状态管理是构建响应式用户界面的关键。通过一系列装饰器和指令,ArkTS 提供了一种简捷而有效的方式来处理组件的状态和数据流。

@State 装饰器用于定义状态变量,它赋予变量状态属性,使其与自定义组件的渲染紧密绑定。当状态发生改变时,UI 会自动同步更新。这类变量是私有的,仅能在组件内部访问。在声明状态变量时,需要指定其类型和初始值,也可以通过命名参数由父组件进行初始化。

其特性包括:
@State 变量传递/访问规则如下:
初始化规则如下图所示:


图 1 初始化规则

注意,并非所有状态变量的变更都会导致 UI 更新,只有框架能观察到的修改才会触发 UI 更新。接下来将阐述哪些修改会被框架观察到,以及框架在观察到变化后的行为。

1) 当 @State 装饰的数据类型为 boolean、string、number 时,可以观察到数值的变化。也就是说,当数据类型为 boolean、string、number 时,如果数据发生改变,那么页面也会同步更新。示例如下:
@Entry
@Component
struct State_demo {
  @State bool: boolean = false;
  @State message: string = '你好';
  @State num: number = 1;

  build() {
    Row() {
      Column() {
        Button('修改数据')
          .onClick(() => {
            this.bool = !this.bool;
            this.message = '数据被修改了';
            this.num++;
          });

        // 修改string类型
        Text(this.message)
          .fontSize(20);

        // 修改number类型
        Text(this.num.toString())
          .fontSize(20);

        // 修改boolean类型
        Text(this.bool.toString())
          .fontSize(20);
      }
      .width('100%')
      .height('100%');
    }
  }
}
在上述示例代码中,定义了 3 种数据类型,通过单击 Button 按钮来进行数据修改。需要注意的是,在 ArkTS 中所有需要渲染出来的内容必须是 string 格式,因此将 number 和 boolean 类型的数据通过 toString() 方法转换成字符串。效果如图 2 和图 3 所示。


图 2 @State 装饰器1

图 3 @State装饰器2

2) 当 @State 装饰的数据类型为 class 或者 Object 时,可以观察到自身赋值的变化和其属性赋值的变化,即 Object.keys(observedObject) 返回的所有属性。

示例如下:
// 声明一个 Testclass 类,包含一个字符串类型的 value 属性
class Testclass {
  value: string;

  // 构造函数,接收一个字符串参数并将其赋值给 value 属性
  constructor(value: string) {
    this.value = value;
  }
}

// 声明一个 Model 类,包含两个公共属性:一个字符串类型的 value 和一个 Testclass 类型的 name
class Model {
  public value: string;
  public name: Testclass;

  // 构造函数,接收一个字符串参数和一个 Testclass 实例,并分别赋值给 value 和 name 属性
  constructor(value: string, a: Testclass) {
    this.value = value;
    this.name = a;
  }
}

接下来使用定义的类型来观察数据的变化。首先使用 @State 装饰 Model,然后进行数据修改,以观察是否可以修改数据。示例代码如下:
// 定义一个名为 message 的 @State 变量,类型为 Model,初始值为一个新的 Model 实例,包含字符串 'Hello' 和一个 Testclass 实例,其参数为 'World'
@State message: Model = new Model('Hello', new Testclass('World'));

build() {
  // 创建一个 Row 组件,宽度为 100%
  Row() {
    // 创建一个 Column 组件
    Column() {
      // 创建一个按钮,单击该按钮时修改 message 的值
      Button('修改数据')
        .onClick(() => {
          // 将 message 的值更新为一个新的 Model 实例,包含字符串 'HI' 和一个 Testclass 实例,其参数为 '数据被修改了!'
          this.message = new Model('HI', new Testclass('数据被修改了'));
        });

      // 创建一个文本组件,用于显示 message 的 value 属性值,字体大小为 20
      Text(this.message.value)
        .fontSize(20);

      // 创建一个文本组件,用于显示 message 的 name 属性的 value 属性值,字体大小为 20
      Text(this.message.name.value)
        .fontSize(20);
    }
    .width('100%')   // 设置 Column 组件的宽度为 100%
    .height('100%'); // 设置 Row 组件的高度为 100%
  }
}
界面效果如图 4 和图 5 所示。


图 4 @State装饰器3

图 5 @State装饰器4

说明当 @State 装饰的是 class 类型时,数据的变化是可以被观察到的。

如果只修改 @State 装饰的某一个属性呢?示例代码如下:
// 创建一个名为 message 的 Model 实例,包含两个属性:value 和 name
@State message: Model = new Model('Hello', new Testclass('World'));

build() {
  // 创建一个 Row 布局
  Row() {
    // 创建一个 Column 布局
    Column() {
      // 创建一个按钮,单击该按钮后修改 message 的值和 name 的值
      Button('修改数据')
        .onClick(() => {
          this.message.value = 'HI';
          this.message.name.value = '数据被修改了';
        });

      // 显示 message 的 value 属性值,字体大小为 20
      Text(this.message.value)
        .fontSize(20);

      // 显示 message 的 name 属性值,字体大小为 20
      Text(this.message.name.value)
        .fontSize(20);
    }
    // 设置 Column 布局的宽度为 100%
    .width('100%')
    // 设置 Row 布局的高度为 100%
    .height('100%');
  }
}
在示例代码中,我们通过以下代码来修改 @State 装饰的数据:
this.message.value = 'HI'
this.message.name.value = '数据被修改了'
效果图见图 4 和图 5,发现数据被修改后依旧可以被观察到。

3) 当装饰的对象是 array 时,可以观察到数组本身的赋值和添加、删除、更新数组的变化。

数组的操作在开发的过程中是比较常见的,通过下面的代码示例将会更好地理解关于数组的操作方案。
// 第一步:声明一个 Arrayclass 类,包含一个数值类型的属性 value
class Arrayclass {
  value: number;

  // 构造函数,用于初始化 Arrayclass 实例的 value 属性
  constructor(value: number) {
    this.value = value;
  }
}

// 第二步:使用 @State 装饰器创建一个 Arrayclass 类型的数组 cousomArray,并初始化为包含 4 个元素的数组
@State cousomArray: Arrayclass[] = [
  new Arrayclass(1),
  new Arrayclass(2),
  new Arrayclass(3),
  new Arrayclass(4)
];

// 第三步:对数组进行操作
build() {
  Column() {
    // 赋值操作:单击按钮后,将 cousomArray 数组重新赋值为只包含一个元素(值为 11)的新数组
    Button('赋值')
      .onClick(() => {
        this.cousomArray = [new Arrayclass(11)];
      });

    // 遍历 cousomArray 数组,显示每个元素的 value 值
    ForEach(this.cousomArray, (item: Arrayclass, index: number) => {
      Text(item.value.toString())
        .fontSize(20);
    });
  }
  .width('100%');
}
注意,代码中用了一个新的属性 ForEach ,这个属性的作用是遍历数组,将数组的每一项内容进行展示。

案例代码主要是实现当单击按钮时,将自定义数组对象的内容进行替换,效果如图 6 和图 7 所示。


图 6 数组的赋值操作1

图 7 数组的赋值操作2

修改数组中的某一项数据,代码如下:
Button('修改第一项数据').onClick(() => {
    this.cousomArray[0] = new ArrayClass(11)
})
单击按钮直接对数组中的第一项数据进行修改,效果如图 8 和图 9 所示。


图 8 数组的替换操作1

图 9 数组的替换操作2

删除数组中的数据,代码如下:
Button('删除数据').onClick(()=>{
   this.cousomArray.pop()
})
JavaScript 基础的读者应该很清楚,案例中我们使用了 pop() 方法对数组进行数据的删除操作,效果如图 10 和图 11 所示。


图 10 数组的删除操作1

图 11 数组的删除操作2

为数组新增数据,代码如下:
Button('新增数据').onClick(()=>{
  this.cousomArray.push(new ArrayClass(20))
})
采用数组的操作方法 push 为 cousomArray 这个数组项新增了一个数据,效果如图 12 和图 13 所示。


图 12 数组的新增操作1

图 13 数组的新增操作2

我们通过 .value 来获取数组中对应的数据,然后直接进行赋值操作,是否可以呢?直接对数组中的属性进行赋值,代码如下:
Button('直接赋值').onClick(()=>{
  this.cousomArray[0].value = 10
})
效果如下图所示。可以发现这种赋值方式是不可以的。


图 14 对数组中的属性进行赋值

相关文章