1# 如何监听多层状态变化 2 3## 场景说明 4应用开发过程中,当希望通过状态变量控制页面刷新时,大家通常想到的就是装饰器@State,但是在嵌套场景下,单单使用@State并不能监听到变量的状态变化,这就引出了@Observed/@ObjectLink装饰器。本文就为大家介绍如何配合使用@State、@Observed、@ObjectLink三个装饰器监听多层状态变化。 5 6## 概念原理 7在讲解具体操作前,大家先理解以下几个概念: 8- 第一层状态变化:指不包含嵌套关系的变量的变化,比如string、number、boolean等基础数据类型的状态变化,以及嵌套结构中第一层变量的状态变化。 9 10- 多层状态变化:指包含嵌套关系的二层及以下变量的变化,比如嵌套类中被嵌套类的成员变量的状态变化,嵌套数组中被嵌套数组的状态变化等。 11 12第一层变量的状态变化可以用@State监听,二层及以下变量的状态变化则需要使用@Observed/@ObjectLink监听。以嵌套结构举例,如下图: 13 14 15 16 17为便于理解,通过以下例子具体说明单层和多层状态变化: 18```ts 19class ClassB { 20 public c: number; 21 22 constructor(c: number) { 23 this.c = c; 24 } 25} 26 27class ClassA { 28 // ClassA成员变量的类型为ClassB,ClassB为被嵌套类 29 public b: ClassB; 30 31 constructor(b: ClassB) { 32 this.b = b; 33 } 34} 35 36a: ClassA 37// 变量b为ClassA的成员变量,为第一层变量,所以变量b的状态变化即为第一层状态变化 38this.a.b = new ClassB(0) 39// 变量c为被嵌套类ClassB的成员变量,变量c的状态变化即为第二层状态变化 40this.a.b.c = 5 41``` 42 43## 监听第一层状态变化 44监听第一层状态变化可以使用@State修饰变量,变量发生变化后即可同步刷新UI,这是大家最常用的场景,为便于理解,此处举例说明一下: 45```ts 46class ClassA { 47 public a:number 48 49 constructor(a:number) { 50 this.a = a; 51 } 52} 53 54@Entry 55@Component 56struct ViewA { 57 // 使用@State修饰变量class_A,以监听其变化 58 @State class_A: ClassA = new ClassA(0); 59 60 build() { 61 Column() { 62 Row(){ 63 Button(`第一层变量+1`) 64 .margin({top:10,right:20}) 65 .backgroundColor('#E8A027') 66 .onClick(() => { 67 // class_A的成员变量a加1,class_A发生变化 68 this.class_A.a += 1; 69 }) 70 // 将第一层变量在UI呈现出来 71 Text(`${this.class_A.a}`) 72 } 73 .margin({top:50}) 74 75 Row(){ 76 Button(`第一层变量变为2`) 77 .margin({top:10,right:20}) 78 .onClick(() => { 79 // 将新的ClassA实例赋值给class_A,class_A发生变化 80 this.class_A = new ClassA(2); 81 }) 82 // 将第一层变量在UI呈现出来 83 Text(`${this.class_A.a}`) 84 } 85 } 86 .width('100%') 87 .justifyContent(FlexAlign.Center) 88 } 89} 90``` 91效果如下,如图可以看出第一层变量发生变化后可以实时在UI呈现出来,所以@State可以有效的监听第一层变量的状态变化: 92 93 94 95## 监听多层状态变化 96接下来,我们介绍如何使用@Observed/@ObjectLink监听多层状态变化。 97在第一层状态监听的基础上我们引入ClassB,构造一个嵌套结构,从而具有多层变量,如下: 98```ts 99// 引入ClassB 100class ClassB { 101 public b: number; 102 103 constructor(b: number) { 104 this.b = b; 105 } 106} 107 108 109class ClassA { 110 // ClassA成员变量a的类型为ClassB,从而形成嵌套结构,ClassB的成员变量b为第二层变量 111 public a:ClassB 112 113 constructor(a:ClassB) { 114 this.a = a; 115 } 116} 117``` 118此时我们可以验证一下,如果仅使用@State是否可以监听到第二层变量的变化: 119 120```ts 121// 引入ClassB 122class ClassB { 123 public b: number; 124 125 constructor(b: number) { 126 this.b = b; 127 } 128} 129 130class ClassA { 131 // ClassA成员变量a的类型为ClassB,从而形成嵌套结构,ClassB的成员变量b为第二层变量 132 public a:ClassB 133 134 constructor(a:ClassB) { 135 this.a = a; 136 } 137} 138 139@Entry 140@Component 141struct ViewA { 142 // 使用@State修饰变量class_A 143 @State class_A: ClassA = new ClassA(new ClassB(0)); 144 145 build() { 146 Column() { 147 Row(){ 148 // 点击按钮,第二层变量发生变化 149 Button('第二层变量+1') 150 .margin({top:10,right:20}) 151 .backgroundColor('#E8A027') 152 .onClick(() => { 153 // 第二层变量变化,嵌套类ClassB的成员变量b加1 154 this.class_A.a.b += 1; 155 }) 156 // 将第二层变量在UI呈现出来 157 Text(`${this.class_A.a.b}`) 158 } 159 .margin({top:50}) 160 } 161 .width('100%') 162 .justifyContent(FlexAlign.Center) 163 } 164} 165``` 166效果如下,可以看出当第二层变量发生变化时,UI没有任何变化,所以单纯使用@State不能监听到二层及以下变量的变化: 167 168 169 170接下来我们使用@Observed/@ObjectLink监听本例中第二层变量的变化。 171根据使用规则,需要使用@Observed修饰嵌套类,使用@ObjectLink修饰嵌套类的实例,且@ObjectLink不能在被@Entry修饰的组件中使用,所以我们构建一个子组件,然后在父组件中进行引用,具体代码如下: 172```ts 173// 使用@Observed修饰ClassB 174@Observed 175class ClassB { 176 public b: number; 177 178 constructor(b: number) { 179 this.b = b; 180 } 181} 182 183class ClassA { 184 // ClassA成员变量a的类型为ClassB,从而形成嵌套结构,ClassB的成员变量b为第二层变量 185 public a:ClassB 186 187 constructor(a:ClassB) { 188 this.a = a; 189 } 190} 191 192// 构建子组件ViewB用于承载@ObjectLink修饰的变量 193@Component 194struct ViewB { 195 // 使用@ObjectLink修饰ClassB的实例class_B 196 @ObjectLink class_B: ClassB; 197 build() { 198 Row() { 199 // 将ClassB的成员变量b在UI呈现出来 200 Text(`${this.class_B.b}`) 201 } 202 .margin({top:100}) 203 } 204} 205 206@Entry 207@Component 208struct ViewA { 209 @State class_A: ClassA = new ClassA(new ClassB(0)); 210 211 build() { 212 Column() { 213 ViewB({ class_B: this.class_A.a }) 214 Row(){ 215 // 点击按钮,第二层变量发生变化 216 Button('第二层变量class_B.b加1') 217 .margin({top:10,right:20}) 218 .backgroundColor('#E8A027') 219 .onClick(() => { 220 // 第二层变量变化,嵌套类ClassB的成员变量b加1 221 this.class_A.a.b += 1; 222 }) 223 } 224 .margin({top:50}) 225 } 226 .width('100%') 227 .justifyContent(FlexAlign.Center) 228 } 229} 230``` 231我们来看下效果: 232 233 234 235如图,现在当二层变量发生变化时,可以完美的被监听到,并在UI中刷新出来了。 236 237当然,嵌套数组等也是同样的原理,大家可以参考[官方指南](../application-dev/quick-start/arkts-observed-and-objectlink.md)进行尝试。 238 239## 参考 240[@Observed和@ObjectLink:嵌套类对象属性变化](../application-dev/quick-start/arkts-observed-and-objectlink.md)