鸿蒙-@State装饰器

228 阅读2分钟

是私有的,只能从组件内部访问,在声明时必须指定其类型和本地初始化。初始化也可选择使用命名参数机制从父组件完成初始化。

@State装饰的变量与子组件中的@Prop装饰变量之间建立单向数据同步,与@Link、@ObjectLink装饰变量之间建立双向数据同步。

观察变化

并不是状态变量的所有更改都会引起UI的刷新,只有可以被框架观察到的修改才会引起UI刷新

  • 当装饰的数据类型为boolean、string、number类型时,可以观察到数值的变化

  • 当装饰的数据类型为class或者Object时,可以观察到自身的赋值的变化,和其属性赋值的变化

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

  • 嵌套属性的赋值观察不到

框架行为

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

常见问题

  1. 使用箭头函数改变状态变量未生效

箭头函数体内的this对象,就是定义该函数时所在的作用域指向的对象,而不是使用时所在的作用域指向的对象

将当前this.vm传入,调用代理状态变量的属性赋值

export default class PlayDetailViewModel {
  coverUrl: string = '#00ff00'

  changeCoverUrl= (model:PlayDetailViewModel)=> {
    model.coverUrl = '#00F5FF'
  }

}
import PlayDetailViewModel from './PlayDetailViewModel'

@Entry
@Component
struct PlayDetailPage {
  @State vm: PlayDetailViewModel = new PlayDetailViewModel();

  build() {
    Stack() {
      Text(this.vm.coverUrl).width(100).height(100).backgroundColor(this.vm.coverUrl)
      Row() {
        Button('点击改变颜色')
          .onClick(() => {
            let self = this.vm;
            this.vm.changeCoverUrl(self);
          })
      }
    }
    .width('100%')
    .height('100%')
    .alignContent(Alignment.Top)
  }
}
  1. 状态变量的修改放在构造函数内未生效

在状态管理中,类会被一层“代理”进行包装。当在组件中改变该类的成员变量时,会被该代理进行拦截,在更改数据源中值的同时,也会将变化通知给绑定的组件,从而实现观测变化与触发刷新。

当开发者把修改success的箭头函数放在构造函数中初始化时,此时this指向原本TestModel,还未被代理封装,所以后续触发query事件无法响应变化。

@Entry
@Component
struct Index {
  @State viewModel: TestModel = new TestModel();

  build() {
    Row() {
      Column() {
        Text(this.viewModel.isSuccess ? 'success' : 'failed')
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
          .onClick(() => {
            this.viewModel.query();
          })
      }.width('100%')
    }.height('100%')
  }
}

export class TestModel {
  isSuccess: boolean = false;
  model: Model

  constructor() {
    this.model = new Model(() => {
      this.isSuccess = true;
      console.log(`this.isSuccess: ${this.isSuccess}`);
    })
  }

  query() {
    this.model.query();
  }
}

export class Model {
  callback: () => void

  constructor(cb: () => void) {
    this.callback = cb;
  }

  query() {
    this.callback();
  }
}

当开发者把修改success的箭头函数放在query中时,此时已完成对象初始化和代理封装,此时this指向代理对象,触发query事件可以响应变化。

...

export class TestModel {
  isSuccess: boolean = false;
  model: Model = new Model(() => {
  })

  query() {
    this.model.callback = () => {
      this.isSuccess = true;
    }
    this.model.query();
  }
}

...

  1. 状态变量只能影响其直接绑定的UI组件的刷新
  • 点击按钮改变的是parent.son的值,son是值拷贝,不会受到parent影响
class Parent {
  son: string = '000';
}

@Entry
@Component
struct Test {
  @State son: string = '111';
  @State parent: Parent = new Parent();

  aboutToAppear(): void {
    this.parent.son = this.son;
  }

  build() {
    Column() {
      Text(`${this.son}`);
      Text(`${this.parent.son}`);
      Button('change')
        .onClick(() => {
          this.parent.son = '222';
        })
    }
  }
}
  • 点击按钮parent.son赋值的是新new出来的对象,与初始化时son对象引用地址不一样,所以点击按钮值parent.son的改变不会影响son的改变
class Son {
  son: string = '000';

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

class Parent {
  son: Son = new Son('111');
}

@Entry
@Component
struct Test {
  @State son: Son = new Son('222');
  @State parent: Parent = new Parent();

  aboutToAppear(): void {
    this.parent.son = this.son;
  }

  build() {
    Column() {
      Text(`${this.son.son}`);
      Text(`${this.parent.son.son}`);
      Button('change')
        .onClick(() => {
          this.parent.son = new Son('444');
          this.parent.son.son = '333';
        })
    }
  }
}