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![variable-layers](figures/variable-layers-decorators.png)
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![first-layer-variable](figures/first-layer-variable.gif)
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![second-layer-with-state](figures/second-layer-with-state.gif)
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![second-variable-with-observed-objectlink](figures/second-variable-with-observed-objectlink.gif)
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)